Models based on ‘basic’ indicators of engagement and regularity of study
Loading the required data
daily.counts <- read.csv("Intermediate_results/regularity_of_study/weekly_counts_of_daily_logins_w2-13.csv")
#colnames(daily.counts)
weekly.counts <- daily.counts %>%
select(user_id, W2_cnt:W13_cnt, tot_cnt, weekly_entropy)
# str(weekly.counts)
daily.gaps <- read.csv("Intermediate_results/regularity_of_study/gaps_between_consecutive_logins_w2-13.csv")
# str(daily.gaps)
# daily gaps do not have normal distribution, so, median will be used
# merge weekly counts and median time gap
counts.data <- merge(x = weekly.counts, y = daily.gaps %>% select(user_id, median_gap),
by = 'user_id', all = TRUE)
exam.scores <- read.csv(file = "Intermediate_results/exam_scores_with_student_ids.csv")
# remove email data
exam.scores <- exam.scores %>% select(-2)
# str(exam.scores)
# merge counts data with exam scores
counts.data <- merge(x = counts.data, y = exam.scores, by.x = 'user_id', by.y = 'USER_ID',
all.x = T, all.y = F)
#summary(counts.data)
# 9 NA values for exam scores; remove them
counts.data <- counts.data %>% filter( is.na(SC_FE_TOT)==FALSE )
Model 1: predictors are the same as those used in the latest cluster model
This means that predictors are counts of active days (days when a student had at least one learning session) per week, entropy of weekly active days, and median gap between two consecutive active days.
lm1.data <- counts.data %>% select(-c(tot_cnt, user_id, SC_MT_TOT))
lm1 <- lm(SC_FE_TOT ~ ., data = lm1.data)
summary(lm1)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm1.data)
Residuals:
Min 1Q Median 3Q Max
-23.2397 -6.3099 -0.6918 5.9104 19.9677
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 8.2257 2.3354 3.522 0.00047 ***
W2_cnt 0.4985 0.3431 1.453 0.14685
W3_cnt 0.0926 0.3651 0.254 0.79991
W4_cnt 0.1341 0.3532 0.380 0.70441
W5_cnt -0.3261 0.3228 -1.010 0.31290
W6_cnt -0.1632 0.3676 -0.444 0.65736
W7_cnt 0.5343 0.4188 1.276 0.20266
W8_cnt 1.0919 0.4068 2.685 0.00752 **
W9_cnt 0.1106 0.3862 0.286 0.77475
W10_cnt 1.0810 0.3454 3.130 0.00186 **
W11_cnt 0.1618 0.3891 0.416 0.67768
W12_cnt 0.4559 0.3298 1.383 0.16746
W13_cnt 0.8561 0.2901 2.951 0.00333 **
weekly_entropy 11.6096 9.9681 1.165 0.24475
median_gap -0.2072 0.3777 -0.549 0.58350
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.417 on 462 degrees of freedom
Multiple R-squared: 0.2831, Adjusted R-squared: 0.2614
F-statistic: 13.03 on 14 and 462 DF, p-value: < 2.2e-16
It’s interesting that counts for only 3 weeks are significant and that all three weeks are in the 2nd part of the course (after midterm exam):
- one day more of study in week 8 contributes 1.09 points to the final exam score;
- one day more in week 10 controbutes 1.08 points, and
- one active day more in week 13 adds 0.86 points.
R-squared is 0.283 (adjusted R2: 0.261).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm1)
par(mfrow=c(1,1)) # Change back to 1 x 1

# there are few potential influential points: 80, 50, 459
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:14)
print(cor.test(lm1.data[,c], lm1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm1)
# OK, values below or equal to 2
The assumptions are satisifed, though there are few potentially influential points that might need to be considered if this model is to be used
Model 2: Like Model 1, but instead of weekly counts, uses total number of active days during the course
lm2.data <- counts.data %>% select(tot_cnt, median_gap, weekly_entropy, SC_FE_TOT)
lm2 <- lm(SC_FE_TOT ~ ., data = lm2.data)
summary(lm2)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm2.data)
Residuals:
Min 1Q Median 3Q Max
-25.649 -6.003 -0.660 5.943 20.407
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.84084 2.17939 2.680 0.00762 **
tot_cnt 0.40120 0.04530 8.857 < 2e-16 ***
median_gap -0.05991 0.37409 -0.160 0.87284
weekly_entropy 11.87966 9.90829 1.199 0.23114
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.476 on 473 degrees of freedom
Multiple R-squared: 0.2557, Adjusted R-squared: 0.251
F-statistic: 54.17 on 3 and 473 DF, p-value: < 2.2e-16
The total number of active days is the only significant predictor, and it is highly significant. Each additional active day contributes 0.4 points to the final exam score.
R-squared is 0.2557 (adjusted R2: 0.251).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm2$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm2)
par(mfrow=c(1,1)) # Change back to 1 x 1

# both OK
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:3)
print(cor.test(lm2.data[,c], lm2$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm2)
# OK, values below or equal to 2
All assumptions are satisified.
Model 3: Number of study sessions per week, weekly entropy of study session counts, and time gap between consecutive sessions
Loading the required data
weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
#str(weekly.sessions)
ses.gap.data <- read.csv("Intermediate_results/regularity_of_study/inter-session_time_intervals.csv") #str(ses.gap.data)
lm3.data <- merge(x = weekly.sessions %>% select(count_w2:count_w12, weekly_entropy, user_id),
y = ses.gap.data %>% select(user_id, median_s_gap),
by = 'user_id', all = TRUE)
lm3.data <- merge(x = lm3.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
by.x = 'user_id', by.y = 'USER_ID', all.x = T, all.y = F)
summary(lm3.data)
## remove rows with NAs
lm3.data <- lm3.data %>% filter(is.na(SC_FE_TOT)==FALSE & is.na(median_s_gap)==FALSE)
# ## since some of the predictors are on quite different scales, rescale them
# apply(lm3.data %>% select(-user_id), 2, shapiro.test)
# apply(lm3.data %>% select(-user_id), 2, function(x) length(boxplot.stats(x)$out))
# # all preditors have outliers -> normalization is not advised
# lm3.sc.data <- scale.features(lm3.data %>% select(-c(user_id, SC_FE_TOT)))
# lm3.sc.data <- cbind(lm3.sc.data, SC_FE_TOT=lm3.data$SC_FE_TOT)
# summary(lm3.sc.data)
## the same results are obtained with scaled and unscaled (original) data;
## will keep the results with original data as they are easier to interpret
lm3.data <- lm3.data %>% select(-user_id)
lm3 <- lm(SC_FE_TOT ~ ., data = lm3.data)
summary(lm3)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm3.data)
Residuals:
Min 1Q Median 3Q Max
-21.6292 -5.9597 -0.8594 5.9990 21.2523
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 15.2754010 1.6511046 9.252 < 2e-16 ***
count_w2 0.0053628 0.1110213 0.048 0.96149
count_w3 0.1909662 0.1157368 1.650 0.09962 .
count_w4 -0.0832153 0.1005015 -0.828 0.40810
count_w5 -0.2802013 0.0866320 -3.234 0.00131 **
count_w7 0.2530987 0.1498286 1.689 0.09184 .
count_w8 0.1688238 0.1441518 1.171 0.24214
count_w9 0.1951703 0.1603609 1.217 0.22420
count_w10 0.5158746 0.1080020 4.777 2.4e-06 ***
count_w11 0.3756283 0.1545471 2.431 0.01546 *
count_w12 0.1057666 0.1333451 0.793 0.42808
weekly_entropy 25.4435546 8.4577490 3.008 0.00277 **
median_s_gap -0.0001634 0.0003508 -0.466 0.64167
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.31 on 463 degrees of freedom
Multiple R-squared: 0.2938, Adjusted R-squared: 0.2755
F-statistic: 16.05 on 12 and 463 DF, p-value: < 2.2e-16
Significant predictors:
- session counts in week 5: negative impact (!), an additional session decreases exam score by 0.28
- session counts in week 10: an additional session increases exam score by 0.52
- session counts in week 11: an additional session increases exam score by 0.37
- weekly entropy: if entropy increases by 1, exam score increases by 25; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance
R-squared: 0.294 (adjusted R2: 0.275).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm3$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm3)
par(mfrow=c(1,1)) # Change back to 1 x 1

# mostly fine, but there are few (potentially) influential points: 412, 459, 437, 77
# let's examine them
lm3.data[c(412,459,437,77),]
summary(lm3.data)
# 437 has very low engagement and very high exam score (35)
# 459 has high engagement (at times very high) and zero (0) exam score
# 412 is similar to 459, but not that extreme (7 exam score; less active)
# 77 is almost completely inactive, and has zero (0) exam score
## assumption 4: predictors and residuals are uncorrelated
lm3.data <- lm3.data %>% filter(is.na(median_s_gap)==FALSE)
for(c in 1:12)
print(cor.test(lm3.data[,c], lm3$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm3)
# OK, values below or slightly above 2
Model 4: Total number of study sessions, weekly entropy of study session counts, and time gap between consecutive sessions
lm4.data <- merge(x = weekly.sessions %>% select(user_id, s_total, weekly_entropy),
y = ses.gap.data %>% select(-mad_s_gap),
by = 'user_id', all = TRUE)
lm4.data <- merge(x = lm4.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
by.x = 'user_id', by.y = 'USER_ID', all.x = T, all.y = F)
lm4.data <- lm4.data %>% filter( is.na(SC_FE_TOT)==FALSE ) %>% select(-user_id)
lm4 <- lm(SC_FE_TOT ~ ., data = lm4.data)
summary(lm4)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm4.data)
Residuals:
Min 1Q Median 3Q Max
-29.4102 -6.3591 -0.7392 6.5142 19.4481
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.526e+01 1.658e+00 9.205 < 2e-16 ***
s_total 1.235e-01 1.383e-02 8.926 < 2e-16 ***
weekly_entropy 3.188e+01 8.598e+00 3.708 0.000233 ***
median_s_gap 2.975e-05 3.607e-04 0.082 0.934310
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.599 on 472 degrees of freedom
(1 observation deleted due to missingness)
Multiple R-squared: 0.2292, Adjusted R-squared: 0.2243
F-statistic: 46.78 on 3 and 472 DF, p-value: < 2.2e-16
Significant predictors:
- total number of sessions: an additional session increases exam score by 0.12
- weekly entropy: if entropy increases by 1, exam score increases by 31.88; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance
R-squared: 0.229 (adjusted R2: 0.224).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm4$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm4)
par(mfrow=c(1, 1))

# the Residuals vs Fitted plot suggests that there might be some non-linear realtionship between the outcome and the predictors
# there are also few influential points: 412, 459, 376, 77
# let's examine them
lm4.data[c(412,459,376, 77),]
summary(lm4.data)
# 77 is a clear outlier
# 376 has relatively high engagement (above 3rd quartile), but very low exam score (4)
# 412 and 459 have already been examined before
## assumption 4: predictors and residuals are uncorrelated
lm4.data <- lm4.data %>% filter(is.na(median_s_gap)==FALSE)
for(c in 1:3)
print(cor.test(lm4.data[,c], lm4$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm4)
# OK, values below 2
Model 5: Number of study sessions per week day, and week day entropy of study session counts
Loading the data
weekday.sessions <- read.csv("Intermediate_results/regularity_of_study/weekday_session_props.csv")
#str(weekday.sessions)
lm5.data <- merge(x = weekday.sessions %>% select(1:8, 11),
y = exam.scores %>% select(-SC_MT_TOT),
by.x = "user_id", by.y = "USER_ID",
all.x = TRUE, all.y = FALSE)
# summary(lm5.data)
lm5.data <- lm5.data %>% filter( is.na(SC_FE_TOT)==FALSE ) %>% select(-user_id)
lm5 <- lm(SC_FE_TOT ~ ., data = lm5.data)
summary(lm5)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm5.data)
Residuals:
Min 1Q Median 3Q Max
-31.2357 -6.1439 -0.9892 6.7715 20.2779
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 15.07048 2.26948 6.641 8.69e-11 ***
Sun_count 0.08458 0.09785 0.864 0.387818
Mon_count 0.20397 0.04877 4.182 3.44e-05 ***
Tue_count 0.11681 0.03721 3.140 0.001799 **
Wed_count 0.10067 0.04187 2.405 0.016578 *
Thu_count 0.16141 0.04133 3.905 0.000108 ***
Fri_count 0.12135 0.10226 1.187 0.235970
Sat_count -0.02262 0.12568 -0.180 0.857269
weekday_entropy 17.61256 6.86108 2.567 0.010567 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.663 on 468 degrees of freedom
Multiple R-squared: 0.2307, Adjusted R-squared: 0.2176
F-statistic: 17.55 on 8 and 468 DF, p-value: < 2.2e-16
Significant predictors:
- Monday session counts: an additional Mon session increases exam score by 0.204
- Tuesday session counts: an additional Tue session increases exam score by 0.117
- Wednesday session counts: an additional Wed session increases exam score by 0.1
- Thursday session counts: an additional Thu session increases exam score by 0.161
- weekly entropy: if entropy increases by 1, exam score increases by 17.6; however, entropy cannot increase not nearly that much; this simply confirms that the higher the regularity (high entropy means that weekly counts are almost uniformly distributed), the higher the performance
R-squared: 0.231 (adjusted R2: 0.218).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm5$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm5)
par(mfrow=c(1,1)) # Change back to 1 x 1

# mostly fine, but there are few potentially influential points: usual suspects (412, 459), 230
# let's examine them
lm5.data[c(412,459,230),]
summary(lm5.data)
# 230 has low engagement and very high exam score (35)
# 459 and 412 have already been considered
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:8)
print(cor.test(lm5.data[,c], lm5$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm5)
# OK, values below 2
Models based on ‘advanced’ indicators of engagement and regularity of study
Model 6: Daily resource use
As predictors, use total counts of different kinds of resources students used during their active days (an active day is a day when a student had at least one study session). The types of resources considered:
- video (VIDEO)
- exercises (EXE)
- multiple choice questions (MCQ)
- reading materials (RES)
- metacognitive items (METACOG)
In addition, consider using:
- median_X_cnt - median number of resources of type X used during the student’s active days (X can be VIDEO, EXE, MCQ, RES, METACOG)
- mad_X_cnt - MAD (median absolute deviation) of resources of the type X used during the student’s active days
- days_X_used - number of days when resources of the type X were used
- prop_X_used - proportion of days when resources of the type X were used versus total number of the student’s active days
Loading the data…
res.use.stats <- read.csv("Intermediate_results/regularity_of_study/daily_resource_use_statistics_w2-5_7-12.csv")
#str(res.use.stats)
lm6.data <- merge(res.use.stats, exam.scores, by.x = "user_id", by.y = "USER_ID", all.x = T, all.y = F)
lm6.data <- lm6.data %>% select(-c(user_id, SC_MT_TOT)) %>% filter( is.na(SC_FE_TOT)==FALSE )
First, include only the indicators of engagement (not regularity)
lm6_1.data <- lm6.data %>% select( starts_with("tot"), starts_with("prop"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
ggcorr(lm6_1.data, method = c("complete","spearman"),
# geom = "circle", min_size = 0, max_size = 15,
label = TRUE, label_size = 3.5,
hjust = 0.85, size = 4, layout.exp = 1)

# tot_mcog_cnt and prop_mcog_used are highly correlated, as are tot_video_cnt and prop_video_used, and tot_mcq_cnt and prop_mcq_used
lm6_1.data <- lm6_1.data %>% select(-c(prop_mcog_used, prop_video_used, prop_mcq_used))
# remove the outliers and re-run the model
lm6_1.data <- lm6_1.data[-c(86, 412, 462, 459),]
lm6_1 <- lm(SC_FE_TOT ~., data = lm6_1.data)
summary(lm6_1)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm6_1.data)
Residuals:
Min 1Q Median 3Q Max
-22.0996 -5.9578 -0.1087 6.5627 20.6982
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.505e+01 2.572e+01 2.529 0.0118 *
tot_video_cnt 1.314e-03 8.603e-04 1.528 0.1273
tot_exe_cnt -6.820e-03 1.328e-03 -5.135 4.16e-07 ***
tot_mcq_cnt 8.755e-03 3.661e-03 2.392 0.0172 *
tot_mcog_cnt 9.811e-03 1.281e-02 0.766 0.4441
tot_res_cnt 1.008e-02 1.955e-03 5.156 3.74e-07 ***
prop_exe_used 4.138e-01 3.661e+00 0.113 0.9100
prop_res_used -4.626e+01 2.576e+01 -1.796 0.0732 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.798 on 465 degrees of freedom
Multiple R-squared: 0.1922, Adjusted R-squared: 0.1801
F-statistic: 15.81 on 7 and 465 DF, p-value: < 2.2e-16
Significant predictors:
- total exercise counts (number of exercise-related events during the student’s active days): an additional exercise-related event decreases the final exam score by 0.0066
- total MCQ counts (number of MCQ-related events during the student’s active days): an additional MCQ-related event increases the final exam score by 0.0078
- total number of reading related events: an additional reading-related event decreases the final exam score by 0.0096
R-squared: 0.192 (adjusted R2: 0.180).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm6_1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm6_1)
par(mfrow=c(1, 1))

# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm6_1)

# not that good
# # the plots point to couple of outliers: 86, 412, 462, 459
# # let's check them:
# lm6_1.data[c(86, 412, 462, 459),]
# summary(lm6_1.data)
# # 459 and 462 have zero exam score, inspite of non-negligible number of learning events (especially 459)
# # 412 was highly active, but had very low exam score (7)
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:7)
print(cor.test(lm6_1.data[,c], lm6_1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm6_1)
# it's fine: all below or equal to 2
The assumption of homoscedasticity cannot be considered satisfied (even after removing outliers)
Now, include both indicators of engagement and indicator of regularity
# include those engagment indicators that proved at least slightly relevant in the previous model
# plus mad_X_cnt as indicators of regularity
lm6_2.data <- lm6.data %>% select(tot_mcq_cnt, tot_exe_cnt, tot_res_cnt, prop_res_used,
starts_with("mad"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
plot.correlations(lm6_2.data)

# exclude mad_res_cnt as highly correlated with tot_res_cnt (which proved significant)
lm6_2.data <- lm6_2.data %>% select(-mad_res_cnt)
lm6_2 <- lm(SC_FE_TOT ~., data = lm6_2.data)
summary(lm6_2)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm6_2.data)
Residuals:
Min 1Q Median 3Q Max
-22.9335 -6.1144 -0.1174 6.8471 23.1086
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 66.127636 26.170686 2.527 0.011840 *
tot_mcq_cnt 0.012136 0.003640 3.334 0.000923 ***
tot_exe_cnt -0.006543 0.001360 -4.813 2.01e-06 ***
tot_res_cnt 0.010416 0.002000 5.208 2.87e-07 ***
prop_res_used -47.054649 26.296683 -1.789 0.074201 .
mad_video_cnt -0.007622 0.081861 -0.093 0.925861
mad_exe_cnt -0.003117 0.020326 -0.153 0.878197
mad_mcq_cnt -0.543694 0.379258 -1.434 0.152362
mad_mcog_cnt -0.952299 1.089663 -0.874 0.382599
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.983 on 468 degrees of freedom
Multiple R-squared: 0.1728, Adjusted R-squared: 0.1586
F-statistic: 12.22 on 8 and 468 DF, p-value: 6.383e-16
None of the MAD variables is significant
Model 7: Daily topic focus
As predictors, use total number of learning actions (during active days) with a particular topic focus; possible topic foci:
- ‘ontopic’ - the topic associated with the action is the topic of the current week
- ‘revisiting’ - the topic associated with the action is the topic of one of the previous weeks
- ‘metacognitive’ - the topic associated with the action is one of the following: ‘ORG’, ‘DBOARD’, ‘STRAT’, ‘STUDYKIT’
- ‘orienteering’ - the topic associated with the action is one of the following: ‘HOME’, ‘HOF’, ‘SEARCH’, ‘TEA’, ‘EXAM’, ‘W01’-‘W13’
- ‘project’ - the action is related to project work
In addition, consider including the following basic statistics:
- median_X_cnt - median number of learning actions per active day with a particular topic focus
- mad_X_cnt - MAD of learning actions per active day with a particular topic focus
- X_days - number of days with at least one action with particular topic focus
- X_prop - proportion of days with the given type of topic focus versus total number of active days
Loading the required data…
topic.stats <- read.csv("Intermediate_results/regularity_of_study/topic_counts_statistics_w2-5_7-12.csv")
# str(topic.stats)
lm7.data <- merge(topic.stats, exam.scores, by.x = "user_id", by.y = "USER_ID", all.x = T, all.y = F)
lm7.data <- lm7.data %>% select(-c(user_id, SC_MT_TOT)) %>% filter( is.na(SC_FE_TOT)==FALSE )
First, include only the indicators of engagement (not regularity)
lm7_1.data <- lm7.data %>% select( starts_with("tot"), ends_with("prop"), SC_FE_TOT)
summary(lm7_1.data)
# examine the presence of (high) correlation between the variables
plot.correlations(lm7_1.data)

# exclude tot_orient_cnt and orinet_prop as they are highly correlated with some other variables
lm7_1.data <- lm7_1.data %>% select(-c(tot_orient_cnt, orient_prop))
# exclude tot_prj_cnt, due to high VIF
lm7_1.data <- lm7_1.data %>% select(-tot_prj_cnt)
lm7_1 <- lm(SC_FE_TOT ~ ., data = lm7_1.data)
summary(lm7_1)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm7_1.data)
Residuals:
Min 1Q Median 3Q Max
-22.4947 -6.5546 -0.6803 6.5096 21.4971
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.592e+01 1.040e+01 4.413 1.27e-05 ***
tot_ontopic_cnt 4.854e-03 9.985e-04 4.861 1.60e-06 ***
tot_revisit_cnt -5.237e-03 1.455e-03 -3.600 0.000352 ***
tot_metacog_cnt 1.191e-02 3.565e-03 3.341 0.000901 ***
ontopic_prop 8.082e-01 3.322e+00 0.243 0.807890
revisit_prop 1.940e+00 3.106e+00 0.625 0.532537
metacog_prop -3.469e+01 1.017e+01 -3.410 0.000705 ***
prj_prop 4.500e+00 4.893e+00 0.920 0.358177
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.063 on 469 degrees of freedom
Multiple R-squared: 0.1562, Adjusted R-squared: 0.1436
F-statistic: 12.4 on 7 and 469 DF, p-value: 1.347e-14
Significant predictors:
- total number of prepartion (ontopic) events: an additional ontopic event increases the final exam score by 0.0048
- total number of revisiting events: an additional revisting event decreases the final exam score by 0.0052
- total number of metacognitive events: an additional metacognitive event increases the final exam score by 0.012
- proportion of days with metacognitive events versus total number of active days: increase in this proportion decreases the exam score
R-squared: 0.156 (adjusted R2: 0.145).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm7_1$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm7_1)
par(mfrow=c(1, 1))

# normality is fine
# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm7_1)

# it's fine
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:8)
print(cor.test(lm7_1.data[,c], lm7_1$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm7_1)
# now, it's fine
Now, include both indicators of engagement and indicator of regularity
# include those engagment indicators that proved at least slightly relevant in the previous model
# plus mad_X_cnt as indicators of regularity
lm7_2.data <- lm7.data %>% select(tot_ontopic_cnt, tot_revisit_cnt, tot_metacog_cnt, metacog_prop,
starts_with("mad"), SC_FE_TOT)
# examine the presence of (high) correlation between the variables
plot.correlations(lm7_2.data)

# exclude tot_metacog_cnt as highly correlated with mad_metacog_cnt and mad_orient_cnt
lm7_2.data <- lm7_2.data %>% select(-c(tot_metacog_cnt, mad_orient_cnt))
lm7_2 <- lm(SC_FE_TOT ~., data = lm7_2.data)
summary(lm7_2)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm7_2.data)
Residuals:
Min 1Q Median 3Q Max
-20.586 -6.413 -1.047 6.841 22.230
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.254e+01 9.826e+00 4.330 1.83e-05 ***
tot_ontopic_cnt 7.177e-03 9.323e-04 7.698 8.27e-14 ***
tot_revisit_cnt -3.802e-03 1.352e-03 -2.812 0.00513 **
metacog_prop -2.679e+01 1.019e+01 -2.629 0.00885 **
mad_ontopic_cnt -1.257e-01 4.010e-02 -3.135 0.00182 **
mad_revisit_cnt -9.064e-02 7.364e-02 -1.231 0.21897
mad_metacog_cnt -3.913e-02 1.476e-01 -0.265 0.79102
mad_prj_cnt -7.659e-01 4.124e+00 -0.186 0.85276
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.1 on 469 degrees of freedom
Multiple R-squared: 0.1493, Adjusted R-squared: 0.1366
F-statistic: 11.76 on 7 and 469 DF, p-value: 8.013e-14
The only regularity indicator that proved significant: mad_ontopic_cnt - one unit increase in MAD of ontopic counts leads to a decrease of 0.126 points in the final exam score
R2: 0.1493 (adjusted R2: 0.1366).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm7_2$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm7_2)
par(mfrow=c(1, 1))

# normality is fine
# unclear if homoscedasticity requirement is fulfilled; check using this plot:
check.homoschedasticity(lm7_2)

# not bad
# a few influential points: 60, 54, 202
# and a few outliers: 19, 294, 50
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:7)
print(cor.test(lm7_2.data[,c], lm7_2$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm7_2)
# OK
A few outliers and (potentially) influential points; apart from that, it’s fine
Model 8: Weekly resource use indicators
Indicators are computed at the week level, based on the following principle: a score of one is given to a student (for a given week), if he/she used certain kind of resource (e.g. video) more than the average (median) use of the that resource type in the given week
Loading the data…
res.use.ind <- read.csv("Intermediate_results/regularity_of_study/res_use_indicators_w2-13.csv")
#str(res.use.ind)
lm8.data <- merge(x = res.use.ind, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
#summary(lm8.data)
# remove students who do not have final exam score
lm8.data <- lm8.data %>% filter( is.na(SC_FE_TOT)==FALSE )
lm8.data <- lm8.data %>% select(-user_id)
# examime correlations
plot.correlations(lm8.data)

# video_ind and MCQ_ind are highly correlated, remove one of them
lm8.data <- lm8.data %>% select(-VIDEO_ind)
lm8 <- lm(SC_FE_TOT ~ ., data = lm8.data)
summary(lm8)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm8.data)
Residuals:
Min 1Q Median 3Q Max
-22.7333 -5.8524 -0.1573 6.1593 21.4361
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 16.10045 1.07114 15.031 < 2e-16 ***
MCQ_ind 0.95088 0.16395 5.800 1.22e-08 ***
EXE_ind -0.82529 0.14298 -5.772 1.42e-08 ***
RES_ind 0.60658 0.14833 4.089 5.08e-05 ***
METACOG_ind 0.03796 0.15725 0.241 0.809
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.38 on 472 degrees of freedom
Multiple R-squared: 0.2739, Adjusted R-squared: 0.2678
F-statistic: 44.52 on 4 and 472 DF, p-value: < 2.2e-16
Significant predictors:
- MCQ_ind - a unit increase in this indicator (ie, one week more when a student’s use of MCQs is higher than the average (median) use of MCQ in that week), increases the final exam score by 0.951 points
- EXE_ind - a unit increase of this indicator (ie, one week more when a student’s use of exercises is higher than the average (median) use of exercises in that week), decreases the exam score by 0.825 points
- RES_ind - a unit increase of this indicator (ie, one week more when a student’s use of reading materias is higher than the average (median) use of reading content in that week), increases the exam score by 0.606 points.
R-squared: 0.274 (adjusted R2: 0.268).
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm8$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm8)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:4)
print(cor.test(lm8.data[,c], lm8$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm8)
# it's fine
Model 9: Topic focus indicators
Indicators are computed at the week level, based on the following principle: a score of one is given to a student (for a given week), if his/her number of events related to a particular topic type (e.g. revisiting) was above the average (median) number of events with that topic type in the given week
Weeks 6 and 13 are excluded from these computations, as during these weeks one can expect different behavioral patterns than usual.
Loading the data
topic.ind <- read.csv("Intermediate_results/regularity_of_study/topic_based_indicators_w2-5_7-12.csv")
#str(topic.ind)
lm9.data <- merge(x = topic.ind, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
#summary(lm9.data)
# remove students who do not have final exam score
lm9.data <- lm9.data %>% filter( is.na(SC_FE_TOT)==FALSE )
lm9.data <- lm9.data %>% select(-user_id)
plot.correlations(lm9.data)

# orient_ind and metacog_ind are highly correlated, remove one of them
lm9.data <- lm9.data %>% select(-orient_ind)
lm9 <- lm(SC_FE_TOT ~ ., data = lm9.data)
summary(lm9)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm9.data)
Residuals:
Min 1Q Median 3Q Max
-21.7208 -6.8879 -0.8953 7.0348 23.7086
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 13.2952 1.0797 12.314 < 2e-16 ***
ontopic_ind 0.9303 0.1693 5.496 6.38e-08 ***
revisit_ind -0.2917 0.1714 -1.702 0.0893 .
metacog_ind 0.4606 0.2082 2.212 0.0274 *
prj_ind 0.2421 0.3127 0.774 0.4392
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 9.076 on 472 degrees of freedom
Multiple R-squared: 0.1484, Adjusted R-squared: 0.1412
F-statistic: 20.56 on 4 and 472 DF, p-value: 1.236e-15
Significant predictors:
- ontopic_ind - a unit increase in this indicator (ie, one week more when a student’s number of ‘ontopic’ events is higher than the average (median) number of ‘ontopic’ events in that week), increases the final exam score by 0.9303 points
- metacog_ind - a unit increase in this indicator (ie, one week more when a student’s number of ‘metacognitive’ events is higher than the average (median) number of ‘metacognitive’ events in that week), increases the final exam score by 0.4606 points
R-squared: 0.1484 (adjusted R-squared: 0.1412)
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm9$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm9)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
# there are few outliers (202, 213, 365), but no influential points
# check the outliers
lm9.data[c(202,213,365),]
# very interesting:
# - 202 was highly active but ended up with zero final exam score
# - 213 and 365 were not preparing for lectures, and generaly had low engagement, but did the exam excellently
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:4)
print(cor.test(lm9.data[,c], lm9$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm9)
# it's fine
Model 10: Combine resource use and topic focus indicators
Use those indicators that proved significant in the previous two models (models 8 and 9)
Loading the data
topic.ind <- read.csv("Intermediate_results/regularity_of_study/topic_based_indicators_w2-5_7-12.csv")
res.use.ind <- read.csv("Intermediate_results/regularity_of_study/res_use_indicators_w2-13.csv")
lm10.data <- merge(x = topic.ind %>% select(user_id, ontopic_ind, metacog_ind),
y = res.use.ind %>% select(user_id, MCQ_ind, RES_ind, EXE_ind),
by = "user_id", all = TRUE)
lm10.data <- merge(x = lm10.data, y = exam.scores %>% select(USER_ID, SC_FE_TOT),
by.x = "user_id", by.y = "USER_ID", all.x = TRUE, all.y = FALSE)
summary(lm10.data)
# remove students who do not have final exam score
lm10.data <- lm10.data %>% filter( is.na(SC_FE_TOT)==FALSE )
plot.correlations(lm10.data %>% select(-user_id))

# RES_ind and metacog_ind are highly correlated, remove metacog_ind as it was less significant in the previous models
lm10.data <- lm10.data %>% select(-metacog_ind)
lm10 <- lm(SC_FE_TOT ~ ., data = lm10.data %>% select(-user_id))
summary(lm10)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm10.data %>% select(-user_id))
Residuals:
Min 1Q Median 3Q Max
-21.6264 -5.5145 -0.4311 6.2478 22.6760
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 16.0064 1.0433 15.343 < 2e-16 ***
ontopic_ind 0.4506 0.2055 2.192 0.028843 *
MCQ_ind 0.7235 0.1851 3.908 0.000107 ***
RES_ind 0.5582 0.1492 3.742 0.000205 ***
EXE_ind -0.9275 0.1496 -6.199 1.24e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.339 on 472 degrees of freedom
Multiple R-squared: 0.2812, Adjusted R-squared: 0.2751
F-statistic: 46.15 on 4 and 472 DF, p-value: < 2.2e-16
All 4 predictors are significant; however, only slight improvement in R2 w.r.t. model 8: R-squared: 0.2812 (adjusted R-squared: 0.2751)
Model 11: Extend model 10 with total number of study sessions and entropy of weekly study session counts
Loading the data
weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
lm11.data <- merge(x = lm10.data, y = weekly.sessions %>% select(user_id, s_total, weekly_entropy),
by = 'user_id', all.x = TRUE, all.y = FALSE)
#summary(lm11.data)
lm11.data <- lm11.data[,c(1:5,7,8,6)]
plot.correlations(lm11.data %>% select(-user_id))

# total number of sessions is highly correlated with almost all other variables
lm11.data <- lm11.data %>% select(-s_total)
lm11 <- lm(SC_FE_TOT ~ ., data = lm11.data %>% select(-c(user_id, ontopic_ind)))
summary(lm11)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm11.data %>% select(-c(user_id,
ontopic_ind)))
Residuals:
Min 1Q Median 3Q Max
-24.1085 -5.7500 -0.1444 5.9306 20.1971
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.6142 1.5988 14.770 < 2e-16 ***
MCQ_ind 0.8916 0.1438 6.200 1.23e-09 ***
RES_ind 0.4795 0.1444 3.320 0.000969 ***
EXE_ind -0.9923 0.1406 -7.060 6.00e-12 ***
weekly_entropy 42.6242 7.0907 6.011 3.69e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.077 on 472 degrees of freedom
Multiple R-squared: 0.3255, Adjusted R-squared: 0.3198
F-statistic: 56.94 on 4 and 472 DF, p-value: < 2.2e-16
All 4 predictors that were eventually used for model building - MCQ_ind, EXE_ind, RES_ind, and weekly_entropy - proved highly significant.
R-squared: 0.3255 (adjusted R-squared: 0.3198)
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm11$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm11)
par(mfrow=c(1, 1))

# normality is fulfilled, but the homoscedasticity requirements is questionable
check.homoschedasticity(lm11)

# not good, there outliers and/or influential points
# check the outliers
lm11.data[c(49,294,50),]
# - 294 and 50: low to moderate activity indicators and zero final exam score
# - 230: moderate activity indicators, but excellent exam score
# check influential points
inf.indices <- as.numeric(names(head(sort(cooks.distance(lm11), decreasing = T))))
lm11.data[inf.indices,]
# observations with ordinal numbers 163, 241, 336 should be considered for removal
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:5)
print(cor.test(lm11.data[,c], lm11$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm11)
## ontopic_ind and MCQ_ind have values > 2; remove ontopic_ind as it is not significant
# now (after ontopic_ind was removed), it's fine
If the model is to be used, the outliers should be dealt with.
Model 12: Extend model 10 with number of study sessions per weekday, and weekday entropy of study session counts
In addition to predictors from Model 10 and weekday entropy of study session counts, use, as predictors, study session counts for those week days that proved as significant predictors in Model 5 (Mon, Tue, Wed, Thu).
Loading the data…
weekday.sessions <- read.csv("Intermediate_results/regularity_of_study/weekday_session_props.csv")
lm12.data <- merge(x = lm10.data,
y = weekday.sessions %>% select(user_id, Mon_count, Tue_count, Wed_count,
Thu_count, weekday_entropy),
by = "user_id", all.x = TRUE, all.y = FALSE)
# summary(lm12.data)
lm12.data <- lm12.data[,c(1:5,7:11,6)]
plot.correlations(lm12.data %>% select(-user_id))

lm12 <- lm(SC_FE_TOT ~ ., data = lm12.data %>% select(-c(user_id, ontopic_ind, RES_ind)))
summary(lm12)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm12.data %>% select(-c(user_id,
ontopic_ind, RES_ind)))
Residuals:
Min 1Q Median 3Q Max
-26.9153 -5.6781 -0.2458 5.9728 21.6389
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 22.93612 2.25082 10.190 < 2e-16 ***
MCQ_ind 0.59566 0.15805 3.769 0.000185 ***
EXE_ind -1.00543 0.14347 -7.008 8.45e-12 ***
Mon_count 0.14117 0.04520 3.123 0.001901 **
Tue_count 0.11040 0.03598 3.068 0.002278 **
Wed_count 0.08320 0.04090 2.034 0.042474 *
Thu_count 0.12923 0.04152 3.112 0.001970 **
weekday_entropy 26.94642 6.01675 4.479 9.45e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 8.085 on 469 degrees of freedom
Multiple R-squared: 0.3285, Adjusted R-squared: 0.3185
F-statistic: 32.78 on 7 and 469 DF, p-value: < 2.2e-16
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm12$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm12)
par(mfrow=c(1, 1))

# both normality and homoscedasticity requirements are fulfilled
# there are few outliers (230, 50, 459), but they do not look that significant
# check influential points
inf.indices <- as.numeric(names(head(sort(cooks.distance(lm12), decreasing = T))))
lm12.data[inf.indices,]
# observations with ordinal numbers 459, 22, 292 should be considered for removal
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:10)
print(cor.test(lm12.data[,c], lm12$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm12)
## ontopic_ind has value > 2; remove it
## now RES_ind stands out with high value; remove it
# now (after ontopic_ind and RES_ind were removed), it's fine
Model 13: Extends model 12 with entropy of weekly study session counts
Loading the data…
weekly.sessions <- read.csv("Intermediate_results/regularity_of_study/weekly_session_props.csv")
lm13.data <- merge(x = lm12.data %>% select(-c(ontopic_ind, RES_ind)),
y = weekly.sessions %>% select(user_id, weekly_entropy),
by = 'user_id', all.x = TRUE, all.y = FALSE)
#summary(lm13.data)
lm13.data <- lm13.data[,c(1:8,10,9)]
plot.correlations(lm13.data %>% select(-user_id))

lm13 <- lm(SC_FE_TOT ~ ., data = lm13.data %>% select(-user_id))
summary(lm13)
Call:
lm(formula = SC_FE_TOT ~ ., data = lm13.data %>% select(-user_id))
Residuals:
Min 1Q Median 3Q Max
-25.4716 -5.6469 -0.3401 5.5829 20.8140
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.49497 2.31501 11.013 < 2e-16 ***
MCQ_ind 0.64099 0.15619 4.104 4.79e-05 ***
EXE_ind -1.05966 0.14208 -7.458 4.29e-13 ***
Mon_count 0.12474 0.04475 2.788 0.005526 **
Tue_count 0.08943 0.03587 2.493 0.013013 *
Wed_count 0.07164 0.04041 1.773 0.076912 .
Thu_count 0.10852 0.04127 2.630 0.008832 **
weekday_entropy 17.59079 6.40524 2.746 0.006259 **
weekly_entropy 29.83472 7.72548 3.862 0.000128 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.968 on 468 degrees of freedom
Multiple R-squared: 0.3492, Adjusted R-squared: 0.3381
F-statistic: 31.39 on 8 and 468 DF, p-value: < 2.2e-16
R-squared: 0.349 (adjusted R-squared: 0.338)
Checking if the model satisfies the assumptions for linear regression:
# assumption 1: the mean of residuals is zero
mean(lm13$residuals)
# OK
# assumption 2: homoscedasticity of residuals or equal variance
# assumption 3: Normality of residuals
par(mfrow=c(2, 2))
plot(lm13)
par(mfrow=c(1, 1))

check.homoschedasticity(lm13)

# normality is fulfilled
# homoscedasticity is somewhat questionable - there are outliers and/or influential points
# outliers: 230, 50, 459
# check influential points
inf.indices <- head(sort(cooks.distance(lm13), decreasing = T))
inf.indices
lm13.data[as.numeric(names(inf.indices)),]
# observations with ordinal numbers 459, 163, 336, and 241 should be considered for removal (all have final exam score = zero)
## assumption 4: predictors and residuals are uncorrelated
for(c in 1:10)
print(cor.test(lm13.data[,c], lm13$residuals))
# OK
## assumption 6: no multicolinearity between explanatory variables
vif(lm13)
# it's fine
LS0tCnRpdGxlOiAiUHJlZGljdGl2ZSBtb2RlbHMgYmFzZWQgb24gdmFyaW91cyBpbmRpY2F0b3JzIG9mIHN0dWRlbnQgZW5nYWdlbWVudCBhbmQvb3IgcmVndWxhcml0eSBvZiBzdHVkeSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCgojIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcyBhbmQgZnVuY3Rpb25zCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGNhcikKCiMgZm9yIGNvcnJlbGF0aW9uIHBsb3RzCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2JyaWF0dGUvZ2djb3JyL21hc3Rlci9nZ2NvcnIuUiIpCmBgYAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyMgc29tZSBhdXhpbGlhcnkgZnVuY3Rpb25zCgpwbG90LmNvcnJlbGF0aW9ucyA8LSBmdW5jdGlvbihkYXRhc2V0KSB7CiAgZ2djb3JyKGRhdGFzZXQsIG1ldGhvZCA9IGMoImNvbXBsZXRlIiwic3BlYXJtYW4iKSwgCiAgICAgICAjICAgICAgZ2VvbSA9ICJjaXJjbGUiLCBtaW5fc2l6ZSA9IDAsIG1heF9zaXplID0gMTUsCiAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsX3NpemUgPSAzLjUsCiAgICAgICBoanVzdCA9IDAuODUsIHNpemUgPSA0LCBsYXlvdXQuZXhwID0gMSkKfQoKCiMjIHRoZSBmLiBzY2FsZXMgdGhlIGdpdmVuIGZlYXR1cmUgc2V0IGJ5IHN0YW5kYXJkaXppbmcgdGhlbQojIyBhcyBmZWF0dXJlcyBhcmUgZXhwZWN0ZWQgdG8gaGF2ZSBvdXRsaWVycywgaW5zdGVhZCBvZiB1c2luZyBtZWFuIGFuZCBTRCwgCiMjIG1lZGlhbiBhbmQgSW50ZXJxdWFydGlsZSBSYW5nZSAoSVFSKSBhcmUgdXNlZCwgYXMgc3VnZ2VzdGVkIGhlcmU6CiMjIGh0dHA6Ly9zY2lraXQtbGVhcm4ub3JnL3N0YWJsZS9tb2R1bGVzL3ByZXByb2Nlc3NpbmcuaHRtbCNzY2FsaW5nLWRhdGEtd2l0aC1vdXRsaWVycwpzY2FsZS5mZWF0dXJlcyA8LSBmdW5jdGlvbihmZWF0dXJlcykgewogIG0gPC0gbWF0cml4KG5yb3cgPSBucm93KGZlYXR1cmVzKSwgbmNvbCA9IG5jb2woZmVhdHVyZXMpLCBieXJvdyA9IEZBTFNFKQogIGkgPC0gMQogIGZvcihmIGluIGZlYXR1cmVzKSB7CiAgICBpZiAoIElRUih4ID0gZiwgbmEucm0gPSBUKSAhPSAwICkKICAgICAgbVssaV0gPC0gKGYgLSBtZWRpYW4oZiwgbmEucm0gPSBUKSkvSVFSKGYsIG5hLnJtID0gVCkKICAgIGVsc2UKICAgICAgbVssaV0gPC0gMAogICAgaSA8LSBpICsgMSAgCiAgfQogIHNjYWxlZC5kYXRhIDwtIGRhdGEuZnJhbWUobSkKICBjb2xuYW1lcyhzY2FsZWQuZGF0YSkgPC0gY29sbmFtZXMoZmVhdHVyZXMpCiAgc2NhbGVkLmRhdGEKfQoKCiMjIHRoZSBmLiBkcmF3cyBhIHBsb3QgdGhhdCBhbGxzIGZvciBjaGVja2luZyBpZiB0aGUgaG9tb3NjaGVkYXN0aWNpdHkgcmVxdWlyZW1lbnQgaXMKIyMgc2F0aXNmaWVkOyB3aGF0IHdlIHdhbnQgdG8gc2VlIG9uIHRoZSBwbG90IGlzIHRoYXQgdGhlIHNtb290aCBsaW5lIChyZXNpZHVhbCBkZXZpYXRpb24gZnJvbSB0aGUgYmVzdC1maXQgbGluZSkgbWF0Y2hlcyBhcyBtdWNoIGFzIHBvc3NpYmxlIHRoZSBkYXNoZWQgbGluZSAobm8gZGV2aWF0aW9uIGZyb20gdGhlIGJlc3QtZml0IGxpbmUpCmNoZWNrLmhvbW9zY2hlZGFzdGljaXR5IDwtIGZ1bmN0aW9uKGxtb2QpIHsKICBwbG90KGZpdHRlZChsbW9kKSwgcmVzaWQobG1vZCx0eXBlPSJwZWFyc29uIiksIGNvbD0iYmx1ZSIsIAogICAgIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsIHlsYWIgPSAiUmVzaWR1YWxzIikgCiAgYWJsaW5lKGg9MCxsd2Q9MikKICBsaW5lcyhzbW9vdGguc3BsaW5lKGZpdHRlZChsbW9kKSwgcmVzaWR1YWxzKGxtb2QpKSwgbHdkPTIsIGNvbD0ncmVkJykKfQpgYGAKCgojIE1vZGVscyBiYXNlZCBvbiAnYmFzaWMnIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgcmVndWxhcml0eSBvZiBzdHVkeSAKCkxvYWRpbmcgdGhlIHJlcXVpcmVkIGRhdGEKYGBge3IgcmVzdWx0cz0naGlkZSd9CmRhaWx5LmNvdW50cyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfY291bnRzX29mX2RhaWx5X2xvZ2luc193Mi0xMy5jc3YiKQojY29sbmFtZXMoZGFpbHkuY291bnRzKQoKd2Vla2x5LmNvdW50cyA8LSBkYWlseS5jb3VudHMgJT4lCiAgc2VsZWN0KHVzZXJfaWQsIFcyX2NudDpXMTNfY250LCB0b3RfY250LCB3ZWVrbHlfZW50cm9weSkKIyBzdHIod2Vla2x5LmNvdW50cykKCmRhaWx5LmdhcHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvZ2Fwc19iZXR3ZWVuX2NvbnNlY3V0aXZlX2xvZ2luc193Mi0xMy5jc3YiKQojIHN0cihkYWlseS5nYXBzKQojIGRhaWx5IGdhcHMgZG8gbm90IGhhdmUgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgc28sIG1lZGlhbiB3aWxsIGJlIHVzZWQKCiMgbWVyZ2Ugd2Vla2x5IGNvdW50cyBhbmQgbWVkaWFuIHRpbWUgZ2FwIApjb3VudHMuZGF0YSA8LSBtZXJnZSh4ID0gd2Vla2x5LmNvdW50cywgeSA9IGRhaWx5LmdhcHMgJT4lIHNlbGVjdCh1c2VyX2lkLCBtZWRpYW5fZ2FwKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAndXNlcl9pZCcsIGFsbCA9IFRSVUUpCgpleGFtLnNjb3JlcyA8LSByZWFkLmNzdihmaWxlID0gIkludGVybWVkaWF0ZV9yZXN1bHRzL2V4YW1fc2NvcmVzX3dpdGhfc3R1ZGVudF9pZHMuY3N2IikKIyByZW1vdmUgZW1haWwgZGF0YQpleGFtLnNjb3JlcyA8LSBleGFtLnNjb3JlcyAlPiUgc2VsZWN0KC0yKQojIHN0cihleGFtLnNjb3JlcykKCiMgbWVyZ2UgY291bnRzIGRhdGEgd2l0aCBleGFtIHNjb3Jlcwpjb3VudHMuZGF0YSA8LSBtZXJnZSh4ID0gY291bnRzLmRhdGEsIHkgPSBleGFtLnNjb3JlcywgYnkueCA9ICd1c2VyX2lkJywgYnkueSA9ICdVU0VSX0lEJywgCiAgICAgICAgICAgICAgICAgICAgIGFsbC54ID0gVCwgYWxsLnkgPSBGKQoKI3N1bW1hcnkoY291bnRzLmRhdGEpCiMgOSBOQSB2YWx1ZXMgZm9yIGV4YW0gc2NvcmVzOyByZW1vdmUgdGhlbQpjb3VudHMuZGF0YSA8LSBjb3VudHMuZGF0YSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKIyMjIE1vZGVsIDE6IHByZWRpY3RvcnMgYXJlIHRoZSBzYW1lIGFzIHRob3NlIHVzZWQgaW4gdGhlIGxhdGVzdCBjbHVzdGVyIG1vZGVsCgpUaGlzIG1lYW5zIHRoYXQgcHJlZGljdG9ycyBhcmUgY291bnRzIG9mIGFjdGl2ZSBkYXlzIChkYXlzIHdoZW4gYSBzdHVkZW50IGhhZCBhdCBsZWFzdCBvbmUgbGVhcm5pbmcgc2Vzc2lvbikgcGVyIHdlZWssIGVudHJvcHkgb2Ygd2Vla2x5IGFjdGl2ZSBkYXlzLCBhbmQgbWVkaWFuIGdhcCBiZXR3ZWVuIHR3byBjb25zZWN1dGl2ZSBhY3RpdmUgZGF5cy4gCgpgYGB7cn0KbG0xLmRhdGEgPC0gY291bnRzLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3RfY250LCB1c2VyX2lkLCBTQ19NVF9UT1QpKQpsbTEgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMS5kYXRhKQpzdW1tYXJ5KGxtMSkKYGBgCkl0J3MgaW50ZXJlc3RpbmcgdGhhdCBjb3VudHMgZm9yIG9ubHkgMyB3ZWVrcyBhcmUgc2lnbmlmaWNhbnQgYW5kIHRoYXQgYWxsIHRocmVlIHdlZWtzIGFyZSBpbiB0aGUgMm5kIHBhcnQgb2YgdGhlIGNvdXJzZSAoYWZ0ZXIgbWlkdGVybSBleGFtKTogCgoqIG9uZSBkYXkgbW9yZSBvZiBzdHVkeSBpbiB3ZWVrIDggY29udHJpYnV0ZXMgMS4wOSBwb2ludHMgdG8gdGhlIGZpbmFsIGV4YW0gc2NvcmU7IAoqIG9uZSBkYXkgbW9yZSBpbiB3ZWVrIDEwIGNvbnRyb2J1dGVzIDEuMDggcG9pbnRzLCBhbmQgCiogb25lIGFjdGl2ZSBkYXkgbW9yZSBpbiB3ZWVrIDEzIGFkZHMgMC44NiBwb2ludHMuCgpSLXNxdWFyZWQgaXMgMC4yODMJKGFkanVzdGVkIFIyOiAwLjI2MSkuCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTEkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG0xKQpwYXIobWZyb3c9YygxLDEpKSAjIENoYW5nZSBiYWNrIHRvIDEgeCAxCiMgdGhlcmUgYXJlIGZldyBwb3RlbnRpYWwgaW5mbHVlbnRpYWwgcG9pbnRzOiA4MCwgNTAsIDQ1OQoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjE0KQogIHByaW50KGNvci50ZXN0KGxtMS5kYXRhWyxjXSwgbG0xJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG0xKQojIE9LLCB2YWx1ZXMgYmVsb3cgb3IgZXF1YWwgdG8gMgpgYGAKVGhlIGFzc3VtcHRpb25zIGFyZSBzYXRpc2lmZWQsIHRob3VnaCB0aGVyZSBhcmUgZmV3IHBvdGVudGlhbGx5IGluZmx1ZW50aWFsIHBvaW50cyB0aGF0IG1pZ2h0IG5lZWQgdG8gYmUgY29uc2lkZXJlZCBpZiB0aGlzIG1vZGVsIGlzIHRvIGJlIHVzZWQgCgoKCiMjIyBNb2RlbCAyOiBMaWtlIE1vZGVsIDEsIGJ1dCBpbnN0ZWFkIG9mIHdlZWtseSBjb3VudHMsIHVzZXMgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzIGR1cmluZyB0aGUgY291cnNlCgpgYGB7cn0KbG0yLmRhdGEgPC0gY291bnRzLmRhdGEgJT4lIHNlbGVjdCh0b3RfY250LCBtZWRpYW5fZ2FwLCB3ZWVrbHlfZW50cm9weSwgU0NfRkVfVE9UKQpsbTIgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMi5kYXRhKQpzdW1tYXJ5KGxtMikKYGBgCgpUaGUgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzIGlzIHRoZSBvbmx5IHNpZ25pZmljYW50IHByZWRpY3RvciwgYW5kIGl0IGlzIGhpZ2hseSBzaWduaWZpY2FudC4gRWFjaCBhZGRpdGlvbmFsIGFjdGl2ZSBkYXkgY29udHJpYnV0ZXMgMC40IHBvaW50cyB0byB0aGUgZmluYWwgZXhhbSBzY29yZS4KClItc3F1YXJlZCBpcyAgMC4yNTU3CShhZGp1c3RlZCBSMjogMC4yNTEpLiAKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtMiRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTIpCnBhcihtZnJvdz1jKDEsMSkpICMgQ2hhbmdlIGJhY2sgdG8gMSB4IDEKIyBib3RoIE9LCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6MykKICBwcmludChjb3IudGVzdChsbTIuZGF0YVssY10sIGxtMiRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtMikKIyBPSywgdmFsdWVzIGJlbG93IG9yIGVxdWFsIHRvIDIKYGBgCkFsbCBhc3N1bXB0aW9ucyBhcmUgc2F0aXNpZmllZC4KCgojIyMgTW9kZWwgMzogTnVtYmVyIG9mIHN0dWR5IHNlc3Npb25zIHBlciB3ZWVrLCB3ZWVrbHkgZW50cm9weSBvZiBzdHVkeSBzZXNzaW9uIGNvdW50cywgYW5kIHRpbWUgZ2FwIGJldHdlZW4gY29uc2VjdXRpdmUgc2Vzc2lvbnMKCkxvYWRpbmcgdGhlIHJlcXVpcmVkIGRhdGEKYGBge3IgcmVzdWx0cz0naGlkZSd9CndlZWtseS5zZXNzaW9ucyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfc2Vzc2lvbl9wcm9wcy5jc3YiKQojc3RyKHdlZWtseS5zZXNzaW9ucykKCnNlcy5nYXAuZGF0YSA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS9pbnRlci1zZXNzaW9uX3RpbWVfaW50ZXJ2YWxzLmNzdiIpICNzdHIoc2VzLmdhcC5kYXRhKQoKbG0zLmRhdGEgPC0gbWVyZ2UoeCA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KGNvdW50X3cyOmNvdW50X3cxMiwgd2Vla2x5X2VudHJvcHksIHVzZXJfaWQpLAogICAgICAgICAgICAgICAgICB5ID0gc2VzLmdhcC5kYXRhICU+JSBzZWxlY3QodXNlcl9pZCwgbWVkaWFuX3NfZ2FwKSwKICAgICAgICAgICAgICAgICAgYnkgPSAndXNlcl9pZCcsIGFsbCA9IFRSVUUpCmxtMy5kYXRhIDwtIG1lcmdlKHggPSBsbTMuZGF0YSwgeSA9IGV4YW0uc2NvcmVzICU+JSBzZWxlY3QoVVNFUl9JRCwgU0NfRkVfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICd1c2VyX2lkJywgYnkueSA9ICdVU0VSX0lEJywgYWxsLnggPSBULCBhbGwueSA9IEYpCnN1bW1hcnkobG0zLmRhdGEpCgojIyByZW1vdmUgcm93cyB3aXRoIE5BcwpsbTMuZGF0YSA8LSBsbTMuZGF0YSAlPiUgZmlsdGVyKGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICYgaXMubmEobWVkaWFuX3NfZ2FwKT09RkFMU0UpIAoKIyAjIyBzaW5jZSBzb21lIG9mIHRoZSBwcmVkaWN0b3JzIGFyZSBvbiBxdWl0ZSBkaWZmZXJlbnQgc2NhbGVzLCByZXNjYWxlIHRoZW0KIyBhcHBseShsbTMuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSwgMiwgc2hhcGlyby50ZXN0KQojIGFwcGx5KGxtMy5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpLCAyLCBmdW5jdGlvbih4KSBsZW5ndGgoYm94cGxvdC5zdGF0cyh4KSRvdXQpKQojICMgYWxsIHByZWRpdG9ycyBoYXZlIG91dGxpZXJzIC0+IG5vcm1hbGl6YXRpb24gaXMgbm90IGFkdmlzZWQKIyBsbTMuc2MuZGF0YSA8LSBzY2FsZS5mZWF0dXJlcyhsbTMuZGF0YSAlPiUgc2VsZWN0KC1jKHVzZXJfaWQsIFNDX0ZFX1RPVCkpKQojIGxtMy5zYy5kYXRhIDwtIGNiaW5kKGxtMy5zYy5kYXRhLCBTQ19GRV9UT1Q9bG0zLmRhdGEkU0NfRkVfVE9UKQojIHN1bW1hcnkobG0zLnNjLmRhdGEpCgojIyB0aGUgc2FtZSByZXN1bHRzIGFyZSBvYnRhaW5lZCB3aXRoIHNjYWxlZCBhbmQgdW5zY2FsZWQgKG9yaWdpbmFsKSBkYXRhOwojIyB3aWxsIGtlZXAgdGhlIHJlc3VsdHMgd2l0aCBvcmlnaW5hbCBkYXRhIGFzIHRoZXkgYXJlIGVhc2llciB0byBpbnRlcnByZXQKCmxtMy5kYXRhIDwtIGxtMy5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpCmBgYAoKYGBge3J9CmxtMyA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG0zLmRhdGEpCnN1bW1hcnkobG0zKQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHNlc3Npb24gY291bnRzIGluIHdlZWsgNTogbmVnYXRpdmUgaW1wYWN0ICghKSwgYW4gYWRkaXRpb25hbCBzZXNzaW9uIGRlY3JlYXNlcyBleGFtIHNjb3JlIGJ5IDAuMjgKKiBzZXNzaW9uIGNvdW50cyBpbiB3ZWVrIDEwOiBhbiBhZGRpdGlvbmFsIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC41MgoqIHNlc3Npb24gY291bnRzIGluIHdlZWsgMTE6IGFuIGFkZGl0aW9uYWwgc2Vzc2lvbiBpbmNyZWFzZXMgZXhhbSBzY29yZSBieSAwLjM3Ciogd2Vla2x5IGVudHJvcHk6IGlmIGVudHJvcHkgaW5jcmVhc2VzIGJ5IDEsIGV4YW0gc2NvcmUgaW5jcmVhc2VzIGJ5IDI1OyBob3dldmVyLCBlbnRyb3B5IGNhbm5vdCBpbmNyZWFzZSBub3QgbmVhcmx5IHRoYXQgbXVjaDsgdGhpcyBzaW1wbHkgY29uZmlybXMgdGhhdCB0aGUgaGlnaGVyIHRoZSByZWd1bGFyaXR5IChoaWdoIGVudHJvcHkgbWVhbnMgdGhhdCB3ZWVrbHkgY291bnRzIGFyZSBhbG1vc3QgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkKSwgdGhlIGhpZ2hlciB0aGUgcGVyZm9ybWFuY2UKClItc3F1YXJlZDogMC4yOTQgKGFkanVzdGVkIFIyOiAwLjI3NSkuCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTMkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG0zKQpwYXIobWZyb3c9YygxLDEpKSAjIENoYW5nZSBiYWNrIHRvIDEgeCAxCiMgbW9zdGx5IGZpbmUsIGJ1dCB0aGVyZSBhcmUgZmV3IChwb3RlbnRpYWxseSkgaW5mbHVlbnRpYWwgcG9pbnRzOiA0MTIsIDQ1OSwgNDM3LCA3NwojIGxldCdzIGV4YW1pbmUgdGhlbQpsbTMuZGF0YVtjKDQxMiw0NTksNDM3LDc3KSxdCnN1bW1hcnkobG0zLmRhdGEpCiMgNDM3IGhhcyB2ZXJ5IGxvdyBlbmdhZ2VtZW50IGFuZCB2ZXJ5IGhpZ2ggZXhhbSBzY29yZSAoMzUpCiMgNDU5IGhhcyBoaWdoIGVuZ2FnZW1lbnQgKGF0IHRpbWVzIHZlcnkgaGlnaCkgYW5kIHplcm8gKDApIGV4YW0gc2NvcmUKIyA0MTIgaXMgc2ltaWxhciB0byA0NTksIGJ1dCBub3QgdGhhdCBleHRyZW1lICg3IGV4YW0gc2NvcmU7IGxlc3MgYWN0aXZlKQojIDc3IGlzIGFsbW9zdCBjb21wbGV0ZWx5IGluYWN0aXZlLCBhbmQgaGFzIHplcm8gKDApIGV4YW0gc2NvcmUKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKbG0zLmRhdGEgPC0gbG0zLmRhdGEgJT4lIGZpbHRlcihpcy5uYShtZWRpYW5fc19nYXApPT1GQUxTRSkKZm9yKGMgaW4gMToxMikKICBwcmludChjb3IudGVzdChsbTMuZGF0YVssY10sIGxtMyRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtMykKIyBPSywgdmFsdWVzIGJlbG93IG9yIHNsaWdodGx5IGFib3ZlIDIKYGBgCgoKIyMjIE1vZGVsIDQ6IFRvdGFsIG51bWJlciBvZiBzdHVkeSBzZXNzaW9ucywgd2Vla2x5IGVudHJvcHkgb2Ygc3R1ZHkgc2Vzc2lvbiBjb3VudHMsIGFuZCB0aW1lIGdhcCBiZXR3ZWVuIGNvbnNlY3V0aXZlIHNlc3Npb25zCgpgYGB7cn0KbG00LmRhdGEgPC0gbWVyZ2UoeCA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KHVzZXJfaWQsIHNfdG90YWwsIHdlZWtseV9lbnRyb3B5KSwKICAgICAgICAgICAgICAgICAgeSA9IHNlcy5nYXAuZGF0YSAlPiUgc2VsZWN0KC1tYWRfc19nYXApLAogICAgICAgICAgICAgICAgICBieSA9ICd1c2VyX2lkJywgYWxsID0gVFJVRSkKbG00LmRhdGEgPC0gbWVyZ2UoeCA9IGxtNC5kYXRhLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gJ3VzZXJfaWQnLCBieS55ID0gJ1VTRVJfSUQnLCBhbGwueCA9IFQsIGFsbC55ID0gRikKCmxtNC5kYXRhIDwtIGxtNC5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkgJT4lIHNlbGVjdCgtdXNlcl9pZCkKYGBgCgpgYGB7cn0KbG00IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTQuZGF0YSkKc3VtbWFyeShsbTQpCmBgYAoKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHRvdGFsIG51bWJlciBvZiBzZXNzaW9uczogYW4gYWRkaXRpb25hbCBzZXNzaW9uIGluY3JlYXNlcyBleGFtIHNjb3JlIGJ5IDAuMTIKKiB3ZWVrbHkgZW50cm9weTogaWYgZW50cm9weSBpbmNyZWFzZXMgYnkgMSwgZXhhbSBzY29yZSBpbmNyZWFzZXMgYnkgMzEuODg7IGhvd2V2ZXIsIGVudHJvcHkgY2Fubm90IGluY3JlYXNlIG5vdCBuZWFybHkgdGhhdCBtdWNoOyB0aGlzIHNpbXBseSBjb25maXJtcyB0aGF0IHRoZSBoaWdoZXIgdGhlIHJlZ3VsYXJpdHkgKGhpZ2ggZW50cm9weSBtZWFucyB0aGF0IHdlZWtseSBjb3VudHMgYXJlIGFsbW9zdCB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQpLCB0aGUgaGlnaGVyIHRoZSBwZXJmb3JtYW5jZQoKUi1zcXVhcmVkOiAwLjIyOSAoYWRqdXN0ZWQgUjI6IDAuMjI0KS4KCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTQkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG00KQpwYXIobWZyb3c9YygxLCAxKSkKIyB0aGUgUmVzaWR1YWxzIHZzIEZpdHRlZCBwbG90IHN1Z2dlc3RzIHRoYXQgdGhlcmUgbWlnaHQgYmUgc29tZSBub24tbGluZWFyIHJlYWx0aW9uc2hpcCBiZXR3ZWVuIHRoZSBvdXRjb21lIGFuZCB0aGUgcHJlZGljdG9ycwojIHRoZXJlIGFyZSBhbHNvIGZldyBpbmZsdWVudGlhbCBwb2ludHM6IDQxMiwgNDU5LCAzNzYsIDc3CiMgbGV0J3MgZXhhbWluZSB0aGVtCmxtNC5kYXRhW2MoNDEyLDQ1OSwzNzYsIDc3KSxdCnN1bW1hcnkobG00LmRhdGEpCiMgNzcgaXMgYSBjbGVhciBvdXRsaWVyCiMgMzc2IGhhcyByZWxhdGl2ZWx5IGhpZ2ggZW5nYWdlbWVudCAoYWJvdmUgM3JkIHF1YXJ0aWxlKSwgYnV0IHZlcnkgbG93IGV4YW0gc2NvcmUgKDQpCiMgNDEyIGFuZCA0NTkgaGF2ZSBhbHJlYWR5IGJlZW4gZXhhbWluZWQgYmVmb3JlCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmxtNC5kYXRhIDwtIGxtNC5kYXRhICU+JSBmaWx0ZXIoaXMubmEobWVkaWFuX3NfZ2FwKT09RkFMU0UpCmZvcihjIGluIDE6MykKICBwcmludChjb3IudGVzdChsbTQuZGF0YVssY10sIGxtNCRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtNCkKIyBPSywgdmFsdWVzIGJlbG93IDIKYGBgCgoKIyMjIE1vZGVsIDU6IE51bWJlciBvZiBzdHVkeSBzZXNzaW9ucyBwZXIgd2VlayBkYXksIGFuZCB3ZWVrIGRheSBlbnRyb3B5IG9mIHN0dWR5IHNlc3Npb24gY291bnRzCgpMb2FkaW5nIHRoZSBkYXRhCmBgYHtyfQp3ZWVrZGF5LnNlc3Npb25zIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3dlZWtkYXlfc2Vzc2lvbl9wcm9wcy5jc3YiKQojc3RyKHdlZWtkYXkuc2Vzc2lvbnMpCgpsbTUuZGF0YSA8LSBtZXJnZSh4ID0gd2Vla2RheS5zZXNzaW9ucyAlPiUgc2VsZWN0KDE6OCwgMTEpLAogICAgICAgICAgICAgICAgICB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdCgtU0NfTVRfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICJ1c2VyX2lkIiwgYnkueSA9ICJVU0VSX0lEIiwKICAgICAgICAgICAgICAgICAgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojIHN1bW1hcnkobG01LmRhdGEpCmxtNS5kYXRhIDwtIGxtNS5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkgJT4lIHNlbGVjdCgtdXNlcl9pZCkKYGBgCgpgYGB7cn0KbG01IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTUuZGF0YSkKc3VtbWFyeShsbTUpCmBgYAoKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIE1vbmRheSBzZXNzaW9uIGNvdW50czogYW4gYWRkaXRpb25hbCBNb24gc2Vzc2lvbiBpbmNyZWFzZXMgZXhhbSBzY29yZSBieSAwLjIwNAoqIFR1ZXNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgVHVlIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xMTcKKiBXZWRuZXNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgV2VkIHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xCiogVGh1cnNkYXkgc2Vzc2lvbiBjb3VudHM6IGFuIGFkZGl0aW9uYWwgVGh1IHNlc3Npb24gaW5jcmVhc2VzIGV4YW0gc2NvcmUgYnkgMC4xNjEKKiB3ZWVrbHkgZW50cm9weTogaWYgZW50cm9weSBpbmNyZWFzZXMgYnkgMSwgZXhhbSBzY29yZSBpbmNyZWFzZXMgYnkgMTcuNjsgaG93ZXZlciwgZW50cm9weSBjYW5ub3QgaW5jcmVhc2Ugbm90IG5lYXJseSB0aGF0IG11Y2g7IHRoaXMgc2ltcGx5IGNvbmZpcm1zIHRoYXQgdGhlIGhpZ2hlciB0aGUgcmVndWxhcml0eSAoaGlnaCBlbnRyb3B5IG1lYW5zIHRoYXQgd2Vla2x5IGNvdW50cyBhcmUgYWxtb3N0IHVuaWZvcm1seSBkaXN0cmlidXRlZCksIHRoZSBoaWdoZXIgdGhlIHBlcmZvcm1hbmNlCgpSLXNxdWFyZWQ6IDAuMjMxIChhZGp1c3RlZCBSMjogMC4yMTgpLgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG01JHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtNSkKcGFyKG1mcm93PWMoMSwxKSkgIyBDaGFuZ2UgYmFjayB0byAxIHggMQojIG1vc3RseSBmaW5lLCBidXQgdGhlcmUgYXJlIGZldyBwb3RlbnRpYWxseSBpbmZsdWVudGlhbCBwb2ludHM6IHVzdWFsIHN1c3BlY3RzICg0MTIsIDQ1OSksIDIzMAojIGxldCdzIGV4YW1pbmUgdGhlbQpsbTUuZGF0YVtjKDQxMiw0NTksMjMwKSxdCnN1bW1hcnkobG01LmRhdGEpCiMgMjMwIGhhcyBsb3cgZW5nYWdlbWVudCBhbmQgdmVyeSBoaWdoIGV4YW0gc2NvcmUgKDM1KQojIDQ1OSBhbmQgNDEyIGhhdmUgYWxyZWFkeSBiZWVuIGNvbnNpZGVyZWQKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo4KQogIHByaW50KGNvci50ZXN0KGxtNS5kYXRhWyxjXSwgbG01JHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG01KQojIE9LLCB2YWx1ZXMgYmVsb3cgMgpgYGAKCgojIE1vZGVscyBiYXNlZCBvbiAnYWR2YW5jZWQnIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgcmVndWxhcml0eSBvZiBzdHVkeSAKCiMjIyBNb2RlbCA2OiBEYWlseSByZXNvdXJjZSB1c2UKCkFzIHByZWRpY3RvcnMsIHVzZSB0b3RhbCBjb3VudHMgb2YgZGlmZmVyZW50IGtpbmRzIG9mIHJlc291cmNlcyBzdHVkZW50cyB1c2VkIGR1cmluZyB0aGVpciBhY3RpdmUgZGF5cyAoYW4gYWN0aXZlIGRheSBpcyBhIGRheSB3aGVuIGEgc3R1ZGVudCBoYWQgYXQgbGVhc3Qgb25lIHN0dWR5IHNlc3Npb24pLiBUaGUgdHlwZXMgb2YgcmVzb3VyY2VzIGNvbnNpZGVyZWQ6IAoKKiB2aWRlbyAoVklERU8pCiogZXhlcmNpc2VzIChFWEUpCiogbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9ucyAoTUNRKQoqIHJlYWRpbmcgbWF0ZXJpYWxzIChSRVMpCiogbWV0YWNvZ25pdGl2ZSBpdGVtcyAoTUVUQUNPRykKCkluIGFkZGl0aW9uLCBjb25zaWRlciB1c2luZzoKCiogbWVkaWFuX1hfY250IC0gbWVkaWFuIG51bWJlciBvZiByZXNvdXJjZXMgb2YgdHlwZSBYIHVzZWQgZHVyaW5nIHRoZSBzdHVkZW50J3MgYWN0aXZlIGRheXMgKFggY2FuIGJlIFZJREVPLCBFWEUsIE1DUSwgUkVTLCBNRVRBQ09HKQoqIG1hZF9YX2NudCAtIE1BRCAobWVkaWFuIGFic29sdXRlIGRldmlhdGlvbikgb2YgcmVzb3VyY2VzIG9mIHRoZSB0eXBlIFggdXNlZCBkdXJpbmcgdGhlIHN0dWRlbnQncyBhY3RpdmUgZGF5cwoqIGRheXNfWF91c2VkIC0gbnVtYmVyIG9mIGRheXMgd2hlbiByZXNvdXJjZXMgb2YgdGhlIHR5cGUgWCB3ZXJlIHVzZWQKKiBwcm9wX1hfdXNlZCAtIHByb3BvcnRpb24gb2YgZGF5cyB3aGVuIHJlc291cmNlcyBvZiB0aGUgdHlwZSBYIHdlcmUgdXNlZCB2ZXJzdXMgdG90YWwgbnVtYmVyIG9mIHRoZSBzdHVkZW50J3MgYWN0aXZlIGRheXMKCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3J9CnJlcy51c2Uuc3RhdHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvZGFpbHlfcmVzb3VyY2VfdXNlX3N0YXRpc3RpY3NfdzItNV83LTEyLmNzdiIpCiNzdHIocmVzLnVzZS5zdGF0cykKCmxtNi5kYXRhIDwtIG1lcmdlKHJlcy51c2Uuc3RhdHMsIGV4YW0uc2NvcmVzLCBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFQsIGFsbC55ID0gRikKbG02LmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBTQ19NVF9UT1QpKSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKCiMjIyMgRmlyc3QsIGluY2x1ZGUgb25seSB0aGUgaW5kaWNhdG9ycyBvZiBlbmdhZ2VtZW50IChub3QgcmVndWxhcml0eSkKYGBge3IgcmVzdWx0cz0naGlkZSd9CmxtNl8xLmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCggc3RhcnRzX3dpdGgoInRvdCIpLCBzdGFydHNfd2l0aCgicHJvcCIpLCBTQ19GRV9UT1QpCgojIGV4YW1pbmUgdGhlIHByZXNlbmNlIG9mIChoaWdoKSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMKZ2djb3JyKGxtNl8xLmRhdGEsIG1ldGhvZCA9IGMoImNvbXBsZXRlIiwic3BlYXJtYW4iKSwgCiAgICAgICAjICAgICAgZ2VvbSA9ICJjaXJjbGUiLCBtaW5fc2l6ZSA9IDAsIG1heF9zaXplID0gMTUsCiAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsX3NpemUgPSAzLjUsCiAgICAgICBoanVzdCA9IDAuODUsIHNpemUgPSA0LCBsYXlvdXQuZXhwID0gMSkKCiMgdG90X21jb2dfY250IGFuZCBwcm9wX21jb2dfdXNlZCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIGFzIGFyZSB0b3RfdmlkZW9fY250IGFuZCBwcm9wX3ZpZGVvX3VzZWQsIGFuZCB0b3RfbWNxX2NudCBhbmQgcHJvcF9tY3FfdXNlZCAKbG02XzEuZGF0YSA8LSBsbTZfMS5kYXRhICU+JSBzZWxlY3QoLWMocHJvcF9tY29nX3VzZWQsIHByb3BfdmlkZW9fdXNlZCwgcHJvcF9tY3FfdXNlZCkpCmBgYAoKYGBge3J9CiMgcmVtb3ZlIHRoZSBvdXRsaWVycyBhbmQgcmUtcnVuIHRoZSBtb2RlbApsbTZfMS5kYXRhIDwtIGxtNl8xLmRhdGFbLWMoODYsIDQxMiwgNDYyLCA0NTkpLF0KbG02XzEgPC0gbG0oU0NfRkVfVE9UIH4uLCBkYXRhID0gbG02XzEuZGF0YSkKc3VtbWFyeShsbTZfMSkKYGBgClNpZ25pZmljYW50IHByZWRpY3RvcnM6IAoKKiB0b3RhbCBleGVyY2lzZSBjb3VudHMgKG51bWJlciBvZiBleGVyY2lzZS1yZWxhdGVkIGV2ZW50cyBkdXJpbmcgdGhlIHN0dWRlbnQncyBhY3RpdmUgZGF5cyk6IGFuIGFkZGl0aW9uYWwgZXhlcmNpc2UtcmVsYXRlZCBldmVudCAqZGVjcmVhc2VzKiB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjAwNjYKKiB0b3RhbCBNQ1EgY291bnRzIChudW1iZXIgb2YgTUNRLXJlbGF0ZWQgZXZlbnRzIGR1cmluZyB0aGUgc3R1ZGVudCdzIGFjdGl2ZSBkYXlzKTogYW4gYWRkaXRpb25hbCBNQ1EtcmVsYXRlZCBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMDc4CiogdG90YWwgbnVtYmVyIG9mIHJlYWRpbmcgcmVsYXRlZCBldmVudHM6IGFuIGFkZGl0aW9uYWwgcmVhZGluZy1yZWxhdGVkIGV2ZW50ICpkZWNyZWFzZXMqIHRoZSBmaW5hbCBleGFtIHNjb3JlIGJ5IDAuMDA5NgoKUi1zcXVhcmVkOiAwLjE5MiAoYWRqdXN0ZWQgUjI6IDAuMTgwKS4KCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTZfMSRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTZfMSkKcGFyKG1mcm93PWMoMSwgMSkpCiMgdW5jbGVhciBpZiBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50IGlzIGZ1bGZpbGxlZDsgY2hlY2sgdXNpbmcgdGhpcyBwbG90OgpjaGVjay5ob21vc2NoZWRhc3RpY2l0eShsbTZfMSkKIyBub3QgdGhhdCBnb29kCgojICMgdGhlIHBsb3RzIHBvaW50IHRvIGNvdXBsZSBvZiBvdXRsaWVyczogODYsIDQxMiwgNDYyLCA0NTkgCiMgIyBsZXQncyBjaGVjayB0aGVtOgojIGxtNl8xLmRhdGFbYyg4NiwgNDEyLCA0NjIsIDQ1OSksXQojIHN1bW1hcnkobG02XzEuZGF0YSkKIyAjIDQ1OSBhbmQgNDYyIGhhdmUgemVybyBleGFtIHNjb3JlLCBpbnNwaXRlIG9mIG5vbi1uZWdsaWdpYmxlIG51bWJlciBvZiBsZWFybmluZyBldmVudHMgKGVzcGVjaWFsbHkgNDU5KQojICMgNDEyIHdhcyBoaWdobHkgYWN0aXZlLCBidXQgaGFkIHZlcnkgbG93IGV4YW0gc2NvcmUgKDcpCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NykKICBwcmludChjb3IudGVzdChsbTZfMS5kYXRhWyxjXSwgbG02XzEkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTZfMSkKIyBpdCdzIGZpbmU6IGFsbCBiZWxvdyBvciBlcXVhbCB0byAyCmBgYApUaGUgYXNzdW1wdGlvbiBvZiBob21vc2NlZGFzdGljaXR5IGNhbm5vdCBiZSBjb25zaWRlcmVkIHNhdGlzZmllZCAoZXZlbiBhZnRlciByZW1vdmluZyBvdXRsaWVycykKCgojIyMjIE5vdywgaW5jbHVkZSBib3RoIGluZGljYXRvcnMgb2YgZW5nYWdlbWVudCBhbmQgaW5kaWNhdG9yIG9mIHJlZ3VsYXJpdHkKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGluY2x1ZGUgdGhvc2UgZW5nYWdtZW50IGluZGljYXRvcnMgdGhhdCBwcm92ZWQgYXQgbGVhc3Qgc2xpZ2h0bHkgcmVsZXZhbnQgaW4gdGhlIHByZXZpb3VzIG1vZGVsCiMgcGx1cyBtYWRfWF9jbnQgYXMgaW5kaWNhdG9ycyBvZiByZWd1bGFyaXR5CmxtNl8yLmRhdGEgPC0gbG02LmRhdGEgJT4lIHNlbGVjdCh0b3RfbWNxX2NudCwgdG90X2V4ZV9jbnQsIHRvdF9yZXNfY250LCBwcm9wX3Jlc191c2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJtYWQiKSwgU0NfRkVfVE9UKQoKIyBleGFtaW5lIHRoZSBwcmVzZW5jZSBvZiAoaGlnaCkgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzCnBsb3QuY29ycmVsYXRpb25zKGxtNl8yLmRhdGEpCgojIGV4Y2x1ZGUgbWFkX3Jlc19jbnQgYXMgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCB0b3RfcmVzX2NudCAod2hpY2ggcHJvdmVkIHNpZ25pZmljYW50KQpsbTZfMi5kYXRhIDwtIGxtNl8yLmRhdGEgJT4lIHNlbGVjdCgtbWFkX3Jlc19jbnQpCmBgYAoKYGBge3J9CmxtNl8yIDwtIGxtKFNDX0ZFX1RPVCB+LiwgZGF0YSA9IGxtNl8yLmRhdGEpCnN1bW1hcnkobG02XzIpCmBgYApOb25lIG9mIHRoZSBNQUQgdmFyaWFibGVzIGlzIHNpZ25pZmljYW50CgoKIyMjIE1vZGVsIDc6IERhaWx5IHRvcGljIGZvY3VzCgpBcyBwcmVkaWN0b3JzLCB1c2UgdG90YWwgbnVtYmVyIG9mIGxlYXJuaW5nIGFjdGlvbnMgKGR1cmluZyBhY3RpdmUgZGF5cykgd2l0aCBhIHBhcnRpY3VsYXIgdG9waWMgZm9jdXM7IHBvc3NpYmxlIHRvcGljIGZvY2k6IAoKKiAnb250b3BpYycgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgdGhlIHRvcGljIG9mIHRoZSBjdXJyZW50IHdlZWsKKiAncmV2aXNpdGluZycgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgdGhlIHRvcGljIG9mIG9uZSBvZiB0aGUgcHJldmlvdXMgd2Vla3MKKiAnbWV0YWNvZ25pdGl2ZScgLSB0aGUgdG9waWMgYXNzb2NpYXRlZCB3aXRoIHRoZSBhY3Rpb24gaXMgb25lIG9mIHRoZSBmb2xsb3dpbmc6ICdPUkcnLCAnREJPQVJEJywgJ1NUUkFUJywgJ1NUVURZS0lUJwoqICdvcmllbnRlZXJpbmcnIC0gdGhlIHRvcGljIGFzc29jaWF0ZWQgd2l0aCB0aGUgYWN0aW9uIGlzIG9uZSBvZiB0aGUgZm9sbG93aW5nOiAnSE9NRScsICdIT0YnLCAnU0VBUkNIJywgJ1RFQScsICdFWEFNJywgJ1cwMSctJ1cxMycKKiAncHJvamVjdCcgLSB0aGUgYWN0aW9uIGlzIHJlbGF0ZWQgdG8gcHJvamVjdCB3b3JrCgpJbiBhZGRpdGlvbiwgY29uc2lkZXIgaW5jbHVkaW5nIHRoZSBmb2xsb3dpbmcgYmFzaWMgc3RhdGlzdGljczoKIAoqIG1lZGlhbl9YX2NudCAtIG1lZGlhbiBudW1iZXIgb2YgbGVhcm5pbmcgYWN0aW9ucyBwZXIgYWN0aXZlIGRheSB3aXRoIGEgcGFydGljdWxhciB0b3BpYyBmb2N1cyAKKiBtYWRfWF9jbnQgLSBNQUQgb2YgbGVhcm5pbmcgYWN0aW9ucyBwZXIgYWN0aXZlIGRheSB3aXRoIGEgcGFydGljdWxhciB0b3BpYyBmb2N1cwoqIFhfZGF5cyAtIG51bWJlciBvZiBkYXlzIHdpdGggYXQgbGVhc3Qgb25lIGFjdGlvbiB3aXRoIHBhcnRpY3VsYXIgdG9waWMgZm9jdXMgCiogWF9wcm9wIC0gcHJvcG9ydGlvbiBvZiBkYXlzIHdpdGggdGhlIGdpdmVuIHR5cGUgb2YgdG9waWMgZm9jdXMgdmVyc3VzIHRvdGFsIG51bWJlciBvZiBhY3RpdmUgZGF5cwoKTG9hZGluZyB0aGUgcmVxdWlyZWQgZGF0YS4uLgpgYGB7cn0KdG9waWMuc3RhdHMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvdG9waWNfY291bnRzX3N0YXRpc3RpY3NfdzItNV83LTEyLmNzdiIpCiMgc3RyKHRvcGljLnN0YXRzKQoKbG03LmRhdGEgPC0gbWVyZ2UodG9waWMuc3RhdHMsIGV4YW0uc2NvcmVzLCBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFQsIGFsbC55ID0gRikKbG03LmRhdGEgPC0gbG03LmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBTQ19NVF9UT1QpKSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCmBgYAoKIyMjIyBGaXJzdCwgaW5jbHVkZSBvbmx5IHRoZSBpbmRpY2F0b3JzIG9mIGVuZ2FnZW1lbnQgKG5vdCByZWd1bGFyaXR5KQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KbG03XzEuZGF0YSA8LSBsbTcuZGF0YSAlPiUgc2VsZWN0KCBzdGFydHNfd2l0aCgidG90IiksIGVuZHNfd2l0aCgicHJvcCIpLCBTQ19GRV9UT1QpCgpzdW1tYXJ5KGxtN18xLmRhdGEpCgojIGV4YW1pbmUgdGhlIHByZXNlbmNlIG9mIChoaWdoKSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMKcGxvdC5jb3JyZWxhdGlvbnMobG03XzEuZGF0YSkKCiMgZXhjbHVkZSB0b3Rfb3JpZW50X2NudCBhbmQgb3JpbmV0X3Byb3AgYXMgdGhleSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBzb21lIG90aGVyIHZhcmlhYmxlcwpsbTdfMS5kYXRhIDwtIGxtN18xLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3Rfb3JpZW50X2NudCwgb3JpZW50X3Byb3ApKQpgYGAKCmBgYHtyfQojIGV4Y2x1ZGUgdG90X3Byal9jbnQsIGR1ZSB0byBoaWdoIFZJRgpsbTdfMS5kYXRhIDwtIGxtN18xLmRhdGEgJT4lIHNlbGVjdCgtdG90X3Byal9jbnQpCmxtN18xIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTdfMS5kYXRhKQpzdW1tYXJ5KGxtN18xKQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczogCgoqIHRvdGFsIG51bWJlciBvZiBwcmVwYXJ0aW9uIChvbnRvcGljKSBldmVudHM6IGFuIGFkZGl0aW9uYWwgb250b3BpYyBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMDQ4CiogdG90YWwgbnVtYmVyIG9mIHJldmlzaXRpbmcgZXZlbnRzOiBhbiBhZGRpdGlvbmFsIHJldmlzdGluZyBldmVudCAqZGVjcmVhc2VzKiB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjAwNTIKKiB0b3RhbCBudW1iZXIgb2YgbWV0YWNvZ25pdGl2ZSBldmVudHM6IGFuIGFkZGl0aW9uYWwgbWV0YWNvZ25pdGl2ZSBldmVudCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC4wMTIKKiBwcm9wb3J0aW9uIG9mIGRheXMgd2l0aCBtZXRhY29nbml0aXZlIGV2ZW50cyB2ZXJzdXMgdG90YWwgbnVtYmVyIG9mIGFjdGl2ZSBkYXlzOiBpbmNyZWFzZSBpbiB0aGlzIHByb3BvcnRpb24gKmRlY3JlYXNlcyogdGhlIGV4YW0gc2NvcmUKClItc3F1YXJlZDogMC4xNTYgKGFkanVzdGVkIFIyOiAwLjE0NSkuCgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG03XzEkcmVzaWR1YWxzKQojIE9LCgojIGFzc3VtcHRpb24gMjogaG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgb3IgZXF1YWwgdmFyaWFuY2UKIyBhc3N1bXB0aW9uIDM6IE5vcm1hbGl0eSBvZiByZXNpZHVhbHMKcGFyKG1mcm93PWMoMiwgMikpCnBsb3QobG03XzEpCnBhcihtZnJvdz1jKDEsIDEpKQojIG5vcm1hbGl0eSBpcyBmaW5lCiMgdW5jbGVhciBpZiBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50IGlzIGZ1bGZpbGxlZDsgY2hlY2sgdXNpbmcgdGhpcyBwbG90OgpjaGVjay5ob21vc2NoZWRhc3RpY2l0eShsbTdfMSkKIyBpdCdzIGZpbmUKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo4KQogIHByaW50KGNvci50ZXN0KGxtN18xLmRhdGFbLGNdLCBsbTdfMSRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtN18xKQojIG5vdywgaXQncyBmaW5lCmBgYAoKIyMjIyBOb3csIGluY2x1ZGUgYm90aCBpbmRpY2F0b3JzIG9mIGVuZ2FnZW1lbnQgYW5kIGluZGljYXRvciBvZiByZWd1bGFyaXR5CgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBpbmNsdWRlIHRob3NlIGVuZ2FnbWVudCBpbmRpY2F0b3JzIHRoYXQgcHJvdmVkIGF0IGxlYXN0IHNsaWdodGx5IHJlbGV2YW50IGluIHRoZSBwcmV2aW91cyBtb2RlbAojIHBsdXMgbWFkX1hfY250IGFzIGluZGljYXRvcnMgb2YgcmVndWxhcml0eQpsbTdfMi5kYXRhIDwtIGxtNy5kYXRhICU+JSBzZWxlY3QodG90X29udG9waWNfY250LCB0b3RfcmV2aXNpdF9jbnQsIHRvdF9tZXRhY29nX2NudCwgbWV0YWNvZ19wcm9wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJtYWQiKSwgU0NfRkVfVE9UKQoKIyBleGFtaW5lIHRoZSBwcmVzZW5jZSBvZiAoaGlnaCkgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzCnBsb3QuY29ycmVsYXRpb25zKGxtN18yLmRhdGEpCgojIGV4Y2x1ZGUgdG90X21ldGFjb2dfY250IGFzIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggbWFkX21ldGFjb2dfY250IGFuZCBtYWRfb3JpZW50X2NudApsbTdfMi5kYXRhIDwtIGxtN18yLmRhdGEgJT4lIHNlbGVjdCgtYyh0b3RfbWV0YWNvZ19jbnQsIG1hZF9vcmllbnRfY250KSkKYGBgCgpgYGB7cn0KbG03XzIgPC0gbG0oU0NfRkVfVE9UIH4uLCBkYXRhID0gbG03XzIuZGF0YSkKc3VtbWFyeShsbTdfMikKYGBgClRoZSBvbmx5IHJlZ3VsYXJpdHkgaW5kaWNhdG9yIHRoYXQgcHJvdmVkIHNpZ25pZmljYW50OiBtYWRfb250b3BpY19jbnQgLSBvbmUgdW5pdCBpbmNyZWFzZSBpbiBNQUQgb2Ygb250b3BpYyBjb3VudHMgbGVhZHMgdG8gYSAqZGVjcmVhc2UqIG9mIDAuMTI2IHBvaW50cyBpbiB0aGUgZmluYWwgZXhhbSBzY29yZSAKClIyOiAwLjE0OTMJKGFkanVzdGVkIFIyOiAwLjEzNjYpLgoKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtN18yJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtN18yKQpwYXIobWZyb3c9YygxLCAxKSkKIyBub3JtYWxpdHkgaXMgZmluZQojIHVuY2xlYXIgaWYgaG9tb3NjZWRhc3RpY2l0eSByZXF1aXJlbWVudCBpcyBmdWxmaWxsZWQ7IGNoZWNrIHVzaW5nIHRoaXMgcGxvdDoKY2hlY2suaG9tb3NjaGVkYXN0aWNpdHkobG03XzIpCiMgbm90IGJhZAoKIyBhIGZldyBpbmZsdWVudGlhbCBwb2ludHM6IDYwLCA1NCwgMjAyCiMgYW5kIGEgZmV3IG91dGxpZXJzOiAxOSwgMjk0LCA1MAoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjcpCiAgcHJpbnQoY29yLnRlc3QobG03XzIuZGF0YVssY10sIGxtN18yJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG03XzIpCiMgT0sKYGBgCkEgZmV3IG91dGxpZXJzIGFuZCAocG90ZW50aWFsbHkpIGluZmx1ZW50aWFsIHBvaW50czsgYXBhcnQgZnJvbSB0aGF0LCBpdCdzIGZpbmUKCgoKIyMjIE1vZGVsIDg6IFdlZWtseSByZXNvdXJjZSB1c2UgaW5kaWNhdG9ycyAKCkluZGljYXRvcnMgYXJlIGNvbXB1dGVkIGF0IHRoZSB3ZWVrIGxldmVsLCBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIHByaW5jaXBsZTogYSBzY29yZSBvZiBvbmUgaXMgZ2l2ZW4gdG8gYSBzdHVkZW50IChmb3IgYSBnaXZlbiB3ZWVrKSwgaWYgaGUvc2hlIHVzZWQgY2VydGFpbiBraW5kIG9mIHJlc291cmNlIChlLmcuIHZpZGVvKSBtb3JlIHRoYW4gdGhlIGF2ZXJhZ2UgKG1lZGlhbikgdXNlIG9mIHRoZSB0aGF0IHJlc291cmNlIHR5cGUgaW4gdGhlIGdpdmVuIHdlZWsKCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3IgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9CnJlcy51c2UuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3Jlc191c2VfaW5kaWNhdG9yc193Mi0xMy5jc3YiKQojc3RyKHJlcy51c2UuaW5kKQoKbG04LmRhdGEgPC0gbWVyZ2UoeCA9IHJlcy51c2UuaW5kLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCiNzdW1tYXJ5KGxtOC5kYXRhKQoKIyByZW1vdmUgc3R1ZGVudHMgd2hvIGRvIG5vdCBoYXZlIGZpbmFsIGV4YW0gc2NvcmUKbG04LmRhdGEgPC0gbG04LmRhdGEgJT4lIGZpbHRlciggaXMubmEoU0NfRkVfVE9UKT09RkFMU0UgKQoKbG04LmRhdGEgPC0gbG04LmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkKCiMgZXhhbWltZSBjb3JyZWxhdGlvbnMKcGxvdC5jb3JyZWxhdGlvbnMobG04LmRhdGEpCgojIHZpZGVvX2luZCBhbmQgTUNRX2luZCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHJlbW92ZSBvbmUgb2YgdGhlbQpsbTguZGF0YSA8LSBsbTguZGF0YSAlPiUgc2VsZWN0KC1WSURFT19pbmQpCmBgYAoKYGBge3J9CmxtOCA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG04LmRhdGEpCnN1bW1hcnkobG04KQpgYGAKU2lnbmlmaWNhbnQgcHJlZGljdG9yczoKCiogTUNRX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgTUNRcyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSB1c2Ugb2YgTUNRIGluIHRoYXQgd2VlayksIGluY3JlYXNlcyB0aGUgZmluYWwgZXhhbSBzY29yZSBieSAwLjk1MSBwb2ludHMgCiogRVhFX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBvZiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgZXhlcmNpc2VzIGlzIGhpZ2hlciB0aGFuIHRoZSBhdmVyYWdlIChtZWRpYW4pIHVzZSBvZiBleGVyY2lzZXMgaW4gdGhhdCB3ZWVrKSwgKmRlY3JlYXNlcyogdGhlIGV4YW0gc2NvcmUgYnkgMC44MjUgcG9pbnRzCiogUkVTX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBvZiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyB1c2Ugb2YgcmVhZGluZyBtYXRlcmlhcyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSB1c2Ugb2YgcmVhZGluZyBjb250ZW50IGluIHRoYXQgd2VlayksIGluY3JlYXNlcyB0aGUgZXhhbSBzY29yZSBieSAwLjYwNiBwb2ludHMuCgpSLXNxdWFyZWQ6IDAuMjc0IChhZGp1c3RlZCBSMjogMC4yNjgpLgoKCkNoZWNraW5nIGlmIHRoZSBtb2RlbCBzYXRpc2ZpZXMgdGhlIGFzc3VtcHRpb25zIGZvciBsaW5lYXIgcmVncmVzc2lvbjoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgYXNzdW1wdGlvbiAxOiB0aGUgbWVhbiBvZiByZXNpZHVhbHMgaXMgemVybwptZWFuKGxtOCRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTgpCnBhcihtZnJvdz1jKDEsIDEpKQojIGJvdGggbm9ybWFsaXR5IGFuZCBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50cyBhcmUgZnVsZmlsbGVkCgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NCkKICBwcmludChjb3IudGVzdChsbTguZGF0YVssY10sIGxtOCRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtOCkKIyBpdCdzIGZpbmUKYGBgCgoKIyMjIE1vZGVsIDk6IFRvcGljIGZvY3VzIGluZGljYXRvcnMKCkluZGljYXRvcnMgYXJlIGNvbXB1dGVkIGF0IHRoZSB3ZWVrIGxldmVsLCBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIHByaW5jaXBsZToKYSBzY29yZSBvZiBvbmUgaXMgZ2l2ZW4gdG8gYSBzdHVkZW50IChmb3IgYSBnaXZlbiB3ZWVrKSwgaWYgaGlzL2hlciBudW1iZXIgb2YgZXZlbnRzIHJlbGF0ZWQgdG8gYSBwYXJ0aWN1bGFyIHRvcGljIHR5cGUgKGUuZy4gcmV2aXNpdGluZykgd2FzIGFib3ZlIHRoZSBhdmVyYWdlIChtZWRpYW4pIG51bWJlciBvZiBldmVudHMgd2l0aCB0aGF0IHRvcGljIHR5cGUgaW4gdGhlIGdpdmVuIHdlZWsKCldlZWtzIDYgYW5kIDEzIGFyZSBleGNsdWRlZCBmcm9tIHRoZXNlIGNvbXB1dGF0aW9ucywgYXMgZHVyaW5nIHRoZXNlIHdlZWtzIG9uZSBjYW4gZXhwZWN0ICBkaWZmZXJlbnQgYmVoYXZpb3JhbCBwYXR0ZXJucyB0aGFuIHVzdWFsLgoKTG9hZGluZyB0aGUgZGF0YQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KdG9waWMuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3RvcGljX2Jhc2VkX2luZGljYXRvcnNfdzItNV83LTEyLmNzdiIpCiNzdHIodG9waWMuaW5kKQoKbG05LmRhdGEgPC0gbWVyZ2UoeCA9IHRvcGljLmluZCwgeSA9IGV4YW0uc2NvcmVzICU+JSBzZWxlY3QoVVNFUl9JRCwgU0NfRkVfVE9UKSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICJ1c2VyX2lkIiwgYnkueSA9ICJVU0VSX0lEIiwgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojc3VtbWFyeShsbTkuZGF0YSkKCiMgcmVtb3ZlIHN0dWRlbnRzIHdobyBkbyBub3QgaGF2ZSBmaW5hbCBleGFtIHNjb3JlCmxtOS5kYXRhIDwtIGxtOS5kYXRhICU+JSBmaWx0ZXIoIGlzLm5hKFNDX0ZFX1RPVCk9PUZBTFNFICkKCmxtOS5kYXRhIDwtIGxtOS5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpCgpwbG90LmNvcnJlbGF0aW9ucyhsbTkuZGF0YSkKCiMgb3JpZW50X2luZCBhbmQgbWV0YWNvZ19pbmQgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCByZW1vdmUgb25lIG9mIHRoZW0KbG05LmRhdGEgPC0gbG05LmRhdGEgJT4lIHNlbGVjdCgtb3JpZW50X2luZCkKYGBgCgpgYGB7cn0KbG05IDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTkuZGF0YSkKc3VtbWFyeShsbTkpCmBgYApTaWduaWZpY2FudCBwcmVkaWN0b3JzOgoKKiBvbnRvcGljX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyBudW1iZXIgb2YgJ29udG9waWMnIGV2ZW50cyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSBudW1iZXIgb2YgJ29udG9waWMnIGV2ZW50cyBpbiB0aGF0IHdlZWspLCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC45MzAzIHBvaW50cyAKKiBtZXRhY29nX2luZCAtIGEgdW5pdCBpbmNyZWFzZSBpbiB0aGlzIGluZGljYXRvciAoaWUsIG9uZSB3ZWVrIG1vcmUgd2hlbiBhIHN0dWRlbnQncyBudW1iZXIgb2YgJ21ldGFjb2duaXRpdmUnIGV2ZW50cyBpcyBoaWdoZXIgdGhhbiB0aGUgYXZlcmFnZSAobWVkaWFuKSBudW1iZXIgb2YgJ21ldGFjb2duaXRpdmUnIGV2ZW50cyBpbiB0aGF0IHdlZWspLCBpbmNyZWFzZXMgdGhlIGZpbmFsIGV4YW0gc2NvcmUgYnkgMC40NjA2IHBvaW50cwoKUi1zcXVhcmVkOiAwLjE0ODQJKGFkanVzdGVkIFItc3F1YXJlZDogMC4xNDEyKQoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG05JHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtOSkKcGFyKG1mcm93PWMoMSwgMSkpCiMgYm90aCBub3JtYWxpdHkgYW5kIGhvbW9zY2VkYXN0aWNpdHkgcmVxdWlyZW1lbnRzIGFyZSBmdWxmaWxsZWQKIyB0aGVyZSBhcmUgZmV3IG91dGxpZXJzICgyMDIsIDIxMywgMzY1KSwgYnV0IG5vIGluZmx1ZW50aWFsIHBvaW50cwoKIyBjaGVjayB0aGUgb3V0bGllcnMKbG05LmRhdGFbYygyMDIsMjEzLDM2NSksXQojIHZlcnkgaW50ZXJlc3Rpbmc6CiMgLSAyMDIgd2FzIGhpZ2hseSBhY3RpdmUgYnV0IGVuZGVkIHVwIHdpdGggemVybyBmaW5hbCBleGFtIHNjb3JlCiMgLSAyMTMgYW5kIDM2NSB3ZXJlIG5vdCBwcmVwYXJpbmcgZm9yIGxlY3R1cmVzLCBhbmQgZ2VuZXJhbHkgaGFkIGxvdyBlbmdhZ2VtZW50LCBidXQgZGlkIHRoZSBleGFtIGV4Y2VsbGVudGx5CgojIyBhc3N1bXB0aW9uIDQ6IHByZWRpY3RvcnMgYW5kIHJlc2lkdWFscyBhcmUgdW5jb3JyZWxhdGVkCmZvcihjIGluIDE6NCkKICBwcmludChjb3IudGVzdChsbTkuZGF0YVssY10sIGxtOSRyZXNpZHVhbHMpKQojIE9LCgojIyBhc3N1bXB0aW9uIDY6IG5vIG11bHRpY29saW5lYXJpdHkgYmV0d2VlbiBleHBsYW5hdG9yeSB2YXJpYWJsZXMKdmlmKGxtOSkKIyBpdCdzIGZpbmUKYGBgCgoKIyMjIE1vZGVsIDEwOiBDb21iaW5lIHJlc291cmNlIHVzZSBhbmQgdG9waWMgZm9jdXMgaW5kaWNhdG9ycwoKVXNlIHRob3NlIGluZGljYXRvcnMgdGhhdCBwcm92ZWQgc2lnbmlmaWNhbnQgaW4gdGhlIHByZXZpb3VzIHR3byBtb2RlbHMgKG1vZGVscyA4IGFuZCA5KQoKTG9hZGluZyB0aGUgZGF0YQpgYGB7ciByZXN1bHRzPSdoaWRlJ30KdG9waWMuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3RvcGljX2Jhc2VkX2luZGljYXRvcnNfdzItNV83LTEyLmNzdiIpCnJlcy51c2UuaW5kIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3Jlc191c2VfaW5kaWNhdG9yc193Mi0xMy5jc3YiKQoKbG0xMC5kYXRhIDwtIG1lcmdlKHggPSB0b3BpYy5pbmQgJT4lIHNlbGVjdCh1c2VyX2lkLCBvbnRvcGljX2luZCwgbWV0YWNvZ19pbmQpLCAKICAgICAgICAgICAgICAgICAgIHkgPSByZXMudXNlLmluZCAlPiUgc2VsZWN0KHVzZXJfaWQsIE1DUV9pbmQsIFJFU19pbmQsIEVYRV9pbmQpLAogICAgICAgICAgICAgICAgICBieSA9ICJ1c2VyX2lkIiwgYWxsID0gVFJVRSkKCmxtMTAuZGF0YSA8LSBtZXJnZSh4ID0gbG0xMC5kYXRhLCB5ID0gZXhhbS5zY29yZXMgJT4lIHNlbGVjdChVU0VSX0lELCBTQ19GRV9UT1QpLAogICAgICAgICAgICAgICAgICBieS54ID0gInVzZXJfaWQiLCBieS55ID0gIlVTRVJfSUQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCnN1bW1hcnkobG0xMC5kYXRhKQoKIyByZW1vdmUgc3R1ZGVudHMgd2hvIGRvIG5vdCBoYXZlIGZpbmFsIGV4YW0gc2NvcmUKbG0xMC5kYXRhIDwtIGxtMTAuZGF0YSAlPiUgZmlsdGVyKCBpcy5uYShTQ19GRV9UT1QpPT1GQUxTRSApCgpwbG90LmNvcnJlbGF0aW9ucyhsbTEwLmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkpCgojIFJFU19pbmQgYW5kIG1ldGFjb2dfaW5kIGFyZSBoaWdobHkgY29ycmVsYXRlZCwgcmVtb3ZlIG1ldGFjb2dfaW5kIGFzIGl0IHdhcyBsZXNzIHNpZ25pZmljYW50IGluIHRoZSBwcmV2aW91cyBtb2RlbHMKbG0xMC5kYXRhIDwtIGxtMTAuZGF0YSAlPiUgc2VsZWN0KC1tZXRhY29nX2luZCkKYGBgCgpgYGB7cn0KbG0xMCA8LSBsbShTQ19GRV9UT1QgfiAuLCBkYXRhID0gbG0xMC5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpKQpzdW1tYXJ5KGxtMTApCmBgYApBbGwgNCBwcmVkaWN0b3JzIGFyZSBzaWduaWZpY2FudDsgaG93ZXZlciwgb25seSBzbGlnaHQgaW1wcm92ZW1lbnQgaW4gUjIgdy5yLnQuIG1vZGVsIDg6ClItc3F1YXJlZDogMC4yODEyCShhZGp1c3RlZCBSLXNxdWFyZWQ6IDAuMjc1MSkKCgojIyMgTW9kZWwgMTE6IEV4dGVuZCBtb2RlbCAxMCB3aXRoIHRvdGFsIG51bWJlciBvZiBzdHVkeSBzZXNzaW9ucyBhbmQgZW50cm9weSBvZiB3ZWVrbHkgc3R1ZHkgc2Vzc2lvbiBjb3VudHMKCkxvYWRpbmcgdGhlIGRhdGEKYGBge3J9CndlZWtseS5zZXNzaW9ucyA8LSByZWFkLmNzdigiSW50ZXJtZWRpYXRlX3Jlc3VsdHMvcmVndWxhcml0eV9vZl9zdHVkeS93ZWVrbHlfc2Vzc2lvbl9wcm9wcy5jc3YiKQoKbG0xMS5kYXRhIDwtIG1lcmdlKHggPSBsbTEwLmRhdGEsIHkgPSB3ZWVrbHkuc2Vzc2lvbnMgJT4lIHNlbGVjdCh1c2VyX2lkLCBzX3RvdGFsLCB3ZWVrbHlfZW50cm9weSksCiAgICAgICAgICAgICAgICAgICBieSA9ICd1c2VyX2lkJywgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFKQojc3VtbWFyeShsbTExLmRhdGEpCmxtMTEuZGF0YSA8LSBsbTExLmRhdGFbLGMoMTo1LDcsOCw2KV0KCnBsb3QuY29ycmVsYXRpb25zKGxtMTEuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSkKCiMgdG90YWwgbnVtYmVyIG9mIHNlc3Npb25zIGlzIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggYWxtb3N0IGFsbCBvdGhlciB2YXJpYWJsZXMKbG0xMS5kYXRhIDwtIGxtMTEuZGF0YSAlPiUgc2VsZWN0KC1zX3RvdGFsKQpgYGAKCmBgYHtyfQpsbTExIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTExLmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBvbnRvcGljX2luZCkpKQpzdW1tYXJ5KGxtMTEpCmBgYApBbGwgNCBwcmVkaWN0b3JzIHRoYXQgd2VyZSBldmVudHVhbGx5IHVzZWQgZm9yIG1vZGVsIGJ1aWxkaW5nIC0gTUNRX2luZCwgRVhFX2luZCwgUkVTX2luZCwgYW5kIHdlZWtseV9lbnRyb3B5IC0gcHJvdmVkIGhpZ2hseSBzaWduaWZpY2FudC4KClItc3F1YXJlZDogMC4zMjU1CShhZGp1c3RlZCBSLXNxdWFyZWQ6IDAuMzE5OCkKCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTExJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtMTEpCnBhcihtZnJvdz1jKDEsIDEpKQojIG5vcm1hbGl0eSBpcyBmdWxmaWxsZWQsIGJ1dCB0aGUgaG9tb3NjZWRhc3RpY2l0eSByZXF1aXJlbWVudHMgaXMgcXVlc3Rpb25hYmxlCmNoZWNrLmhvbW9zY2hlZGFzdGljaXR5KGxtMTEpCiMgbm90IGdvb2QsIHRoZXJlIG91dGxpZXJzIGFuZC9vciBpbmZsdWVudGlhbCBwb2ludHMKCiMgY2hlY2sgdGhlIG91dGxpZXJzCmxtMTEuZGF0YVtjKDQ5LDI5NCw1MCksXQojIC0gMjk0IGFuZCA1MDogbG93IHRvIG1vZGVyYXRlIGFjdGl2aXR5IGluZGljYXRvcnMgYW5kIHplcm8gZmluYWwgZXhhbSBzY29yZQojIC0gMjMwOiBtb2RlcmF0ZSBhY3Rpdml0eSBpbmRpY2F0b3JzLCBidXQgZXhjZWxsZW50IGV4YW0gc2NvcmUKCiMgY2hlY2sgaW5mbHVlbnRpYWwgcG9pbnRzCmluZi5pbmRpY2VzIDwtIGFzLm51bWVyaWMobmFtZXMoaGVhZChzb3J0KGNvb2tzLmRpc3RhbmNlKGxtMTEpLCBkZWNyZWFzaW5nID0gVCkpKSkKbG0xMS5kYXRhW2luZi5pbmRpY2VzLF0KIyBvYnNlcnZhdGlvbnMgd2l0aCBvcmRpbmFsIG51bWJlcnMgIDE2MywgMjQxLCAzMzYgc2hvdWxkIGJlIGNvbnNpZGVyZWQgZm9yIHJlbW92YWwKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMTo1KQogIHByaW50KGNvci50ZXN0KGxtMTEuZGF0YVssY10sIGxtMTEkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTExKQojIyBvbnRvcGljX2luZCBhbmQgTUNRX2luZCBoYXZlIHZhbHVlcyA+IDI7IHJlbW92ZSBvbnRvcGljX2luZCBhcyBpdCBpcyBub3Qgc2lnbmlmaWNhbnQKIyBub3cgKGFmdGVyIG9udG9waWNfaW5kIHdhcyByZW1vdmVkKSwgaXQncyBmaW5lCmBgYApJZiB0aGUgbW9kZWwgaXMgdG8gYmUgdXNlZCwgdGhlIG91dGxpZXJzIHNob3VsZCBiZSBkZWFsdCB3aXRoLgoKCiMjIyBNb2RlbCAxMjogRXh0ZW5kIG1vZGVsIDEwIHdpdGggbnVtYmVyIG9mIHN0dWR5IHNlc3Npb25zIHBlciB3ZWVrZGF5LCBhbmQgd2Vla2RheSBlbnRyb3B5IG9mIHN0dWR5IHNlc3Npb24gY291bnRzCgpJbiBhZGRpdGlvbiB0byBwcmVkaWN0b3JzIGZyb20gTW9kZWwgMTAgYW5kIHdlZWtkYXkgZW50cm9weSBvZiBzdHVkeSBzZXNzaW9uIGNvdW50cywgdXNlLCBhcyBwcmVkaWN0b3JzLCBzdHVkeSBzZXNzaW9uIGNvdW50cyBmb3IgdGhvc2Ugd2VlayBkYXlzIHRoYXQgcHJvdmVkIGFzIHNpZ25pZmljYW50IHByZWRpY3RvcnMgaW4gTW9kZWwgNSAoTW9uLCBUdWUsIFdlZCwgVGh1KS4KCkxvYWRpbmcgdGhlIGRhdGEuLi4KYGBge3J9CndlZWtkYXkuc2Vzc2lvbnMgPC0gcmVhZC5jc3YoIkludGVybWVkaWF0ZV9yZXN1bHRzL3JlZ3VsYXJpdHlfb2Zfc3R1ZHkvd2Vla2RheV9zZXNzaW9uX3Byb3BzLmNzdiIpCgpsbTEyLmRhdGEgPC0gbWVyZ2UoeCA9IGxtMTAuZGF0YSwKICAgICAgICAgICAgICAgICAgIHkgPSB3ZWVrZGF5LnNlc3Npb25zICU+JSBzZWxlY3QodXNlcl9pZCwgTW9uX2NvdW50LCBUdWVfY291bnQsIFdlZF9jb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGh1X2NvdW50LCB3ZWVrZGF5X2VudHJvcHkpLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gInVzZXJfaWQiLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCgojIHN1bW1hcnkobG0xMi5kYXRhKQpsbTEyLmRhdGEgPC0gbG0xMi5kYXRhWyxjKDE6NSw3OjExLDYpXQoKcGxvdC5jb3JyZWxhdGlvbnMobG0xMi5kYXRhICU+JSBzZWxlY3QoLXVzZXJfaWQpKQpgYGAKCmBgYHtyfQpsbTEyIDwtIGxtKFNDX0ZFX1RPVCB+IC4sIGRhdGEgPSBsbTEyLmRhdGEgJT4lIHNlbGVjdCgtYyh1c2VyX2lkLCBvbnRvcGljX2luZCwgUkVTX2luZCkpKQpzdW1tYXJ5KGxtMTIpCmBgYAoKCgpDaGVja2luZyBpZiB0aGUgbW9kZWwgc2F0aXNmaWVzIHRoZSBhc3N1bXB0aW9ucyBmb3IgbGluZWFyIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGFzc3VtcHRpb24gMTogdGhlIG1lYW4gb2YgcmVzaWR1YWxzIGlzIHplcm8KbWVhbihsbTEyJHJlc2lkdWFscykKIyBPSwoKIyBhc3N1bXB0aW9uIDI6IGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIG9yIGVxdWFsIHZhcmlhbmNlCiMgYXNzdW1wdGlvbiAzOiBOb3JtYWxpdHkgb2YgcmVzaWR1YWxzCnBhcihtZnJvdz1jKDIsIDIpKQpwbG90KGxtMTIpCnBhcihtZnJvdz1jKDEsIDEpKQojIGJvdGggbm9ybWFsaXR5IGFuZCBob21vc2NlZGFzdGljaXR5IHJlcXVpcmVtZW50cyBhcmUgZnVsZmlsbGVkCgojIHRoZXJlIGFyZSBmZXcgb3V0bGllcnMgKDIzMCwgNTAsIDQ1OSksIGJ1dCB0aGV5IGRvIG5vdCBsb29rIHRoYXQgc2lnbmlmaWNhbnQKCiMgY2hlY2sgaW5mbHVlbnRpYWwgcG9pbnRzCmluZi5pbmRpY2VzIDwtIGFzLm51bWVyaWMobmFtZXMoaGVhZChzb3J0KGNvb2tzLmRpc3RhbmNlKGxtMTIpLCBkZWNyZWFzaW5nID0gVCkpKSkKbG0xMi5kYXRhW2luZi5pbmRpY2VzLF0KIyBvYnNlcnZhdGlvbnMgd2l0aCBvcmRpbmFsIG51bWJlcnMgIDQ1OSwgMjIsIDI5MiBzaG91bGQgYmUgY29uc2lkZXJlZCBmb3IgcmVtb3ZhbAoKIyMgYXNzdW1wdGlvbiA0OiBwcmVkaWN0b3JzIGFuZCByZXNpZHVhbHMgYXJlIHVuY29ycmVsYXRlZApmb3IoYyBpbiAxOjEwKQogIHByaW50KGNvci50ZXN0KGxtMTIuZGF0YVssY10sIGxtMTIkcmVzaWR1YWxzKSkKIyBPSwoKIyMgYXNzdW1wdGlvbiA2OiBubyBtdWx0aWNvbGluZWFyaXR5IGJldHdlZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzCnZpZihsbTEyKQojIyBvbnRvcGljX2luZCBoYXMgdmFsdWUgPiAyOyByZW1vdmUgaXQKIyMgbm93IFJFU19pbmQgc3RhbmRzIG91dCB3aXRoIGhpZ2ggdmFsdWU7IHJlbW92ZSBpdAojIG5vdyAoYWZ0ZXIgb250b3BpY19pbmQgYW5kIFJFU19pbmQgd2VyZSByZW1vdmVkKSwgaXQncyBmaW5lCmBgYAoKCgojIyMgTW9kZWwgMTM6IEV4dGVuZHMgbW9kZWwgMTIgd2l0aCBlbnRyb3B5IG9mIHdlZWtseSBzdHVkeSBzZXNzaW9uIGNvdW50cwoKTG9hZGluZyB0aGUgZGF0YS4uLgpgYGB7cn0Kd2Vla2x5LnNlc3Npb25zIDwtIHJlYWQuY3N2KCJJbnRlcm1lZGlhdGVfcmVzdWx0cy9yZWd1bGFyaXR5X29mX3N0dWR5L3dlZWtseV9zZXNzaW9uX3Byb3BzLmNzdiIpCgpsbTEzLmRhdGEgPC0gbWVyZ2UoeCA9IGxtMTIuZGF0YSAlPiUgc2VsZWN0KC1jKG9udG9waWNfaW5kLCBSRVNfaW5kKSksIAogICAgICAgICAgICAgICAgICAgeSA9IHdlZWtseS5zZXNzaW9ucyAlPiUgc2VsZWN0KHVzZXJfaWQsIHdlZWtseV9lbnRyb3B5KSwKICAgICAgICAgICAgICAgICAgIGJ5ID0gJ3VzZXJfaWQnLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gRkFMU0UpCiNzdW1tYXJ5KGxtMTMuZGF0YSkKbG0xMy5kYXRhIDwtIGxtMTMuZGF0YVssYygxOjgsMTAsOSldCgpwbG90LmNvcnJlbGF0aW9ucyhsbTEzLmRhdGEgJT4lIHNlbGVjdCgtdXNlcl9pZCkpCmBgYAoKYGBge3J9CmxtMTMgPC0gbG0oU0NfRkVfVE9UIH4gLiwgZGF0YSA9IGxtMTMuZGF0YSAlPiUgc2VsZWN0KC11c2VyX2lkKSkKc3VtbWFyeShsbTEzKQpgYGAKClItc3F1YXJlZDogMC4zNDkgKGFkanVzdGVkIFItc3F1YXJlZDogMC4zMzgpCgoKQ2hlY2tpbmcgaWYgdGhlIG1vZGVsIHNhdGlzZmllcyB0aGUgYXNzdW1wdGlvbnMgZm9yIGxpbmVhciByZWdyZXNzaW9uOgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBhc3N1bXB0aW9uIDE6IHRoZSBtZWFuIG9mIHJlc2lkdWFscyBpcyB6ZXJvCm1lYW4obG0xMyRyZXNpZHVhbHMpCiMgT0sKCiMgYXNzdW1wdGlvbiAyOiBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscyBvciBlcXVhbCB2YXJpYW5jZQojIGFzc3VtcHRpb24gMzogTm9ybWFsaXR5IG9mIHJlc2lkdWFscwpwYXIobWZyb3c9YygyLCAyKSkKcGxvdChsbTEzKQpwYXIobWZyb3c9YygxLCAxKSkKY2hlY2suaG9tb3NjaGVkYXN0aWNpdHkobG0xMykKIyBub3JtYWxpdHkgaXMgZnVsZmlsbGVkCiMgaG9tb3NjZWRhc3RpY2l0eSBpcyBzb21ld2hhdCBxdWVzdGlvbmFibGUgLSB0aGVyZSBhcmUgb3V0bGllcnMgYW5kL29yIGluZmx1ZW50aWFsIHBvaW50cwoKIyBvdXRsaWVyczogMjMwLCA1MCwgNDU5CgojIGNoZWNrIGluZmx1ZW50aWFsIHBvaW50cwppbmYuaW5kaWNlcyA8LSBoZWFkKHNvcnQoY29va3MuZGlzdGFuY2UobG0xMyksIGRlY3JlYXNpbmcgPSBUKSkKaW5mLmluZGljZXMKbG0xMy5kYXRhW2FzLm51bWVyaWMobmFtZXMoaW5mLmluZGljZXMpKSxdCiMgb2JzZXJ2YXRpb25zIHdpdGggb3JkaW5hbCBudW1iZXJzICA0NTksIDE2MywgMzM2LCBhbmQgMjQxIHNob3VsZCBiZSBjb25zaWRlcmVkIGZvciByZW1vdmFsIChhbGwgaGF2ZSBmaW5hbCBleGFtIHNjb3JlID0gemVybykKCiMjIGFzc3VtcHRpb24gNDogcHJlZGljdG9ycyBhbmQgcmVzaWR1YWxzIGFyZSB1bmNvcnJlbGF0ZWQKZm9yKGMgaW4gMToxMCkKICBwcmludChjb3IudGVzdChsbTEzLmRhdGFbLGNdLCBsbTEzJHJlc2lkdWFscykpCiMgT0sKCiMjIGFzc3VtcHRpb24gNjogbm8gbXVsdGljb2xpbmVhcml0eSBiZXR3ZWVuIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwp2aWYobG0xMykKIyBpdCdzIGZpbmUKYGBg